diff --git a/libmat2/archive.py b/libmat2/archive.py index 7aec2b392e526db8e803d51409c036a4dec251c5..924865ab63edea677f09161a9882153693c11928 100644 --- a/libmat2/archive.py +++ b/libmat2/archive.py @@ -13,9 +13,8 @@ from . import abstract, UnknownMemberPolicy, parser_factory # Make pyflakes happy assert Set assert Pattern -assert List -assert Union -assert Text + +# pylint: disable=not-callable,assignment-from-no-return ArchiveClass = Union[zipfile.ZipFile, tarfile.TarFile] ArchiveMember = Union[zipfile.ZipInfo, tarfile.TarInfo] @@ -31,6 +30,12 @@ class ArchiveBasedAbstractParser(abstract.AbstractParser): - Python has two different modules to deal with .tar and .zip files, with similar-but-yet-o-so-different API, so we need to write a ghetto-wrapper to avoid duplicating everything + - The combination of @staticmethod and @abstractstaticmethod is + required because for now, mypy doesn't know that + @abstractstaticmethod is, indeed, a static method. + - Mypy is too dumb (yet) to realise that a type A is valid under + the Union[A, B] constrain, hence the weird `# type: ignore` + annotations. """ def __init__(self, filename): super().__init__(filename) @@ -67,25 +72,30 @@ class ArchiveBasedAbstractParser(abstract.AbstractParser): # pylint: disable=unused-argument,no-self-use return {} # pragma: no cover + @staticmethod @abc.abstractstaticmethod def _get_all_members(archive: ArchiveClass) -> List[ArchiveMember]: """Return all the members of the archive.""" + @staticmethod @abc.abstractstaticmethod def _clean_member(member: ArchiveMember) -> ArchiveMember: """Remove all the metadata for a given member.""" + @staticmethod @abc.abstractstaticmethod def _get_member_meta(member: ArchiveMember) -> Dict[str, str]: """Return all the metadata of a given member.""" + @staticmethod @abc.abstractstaticmethod def _get_member_name(member: ArchiveMember) -> Text: """Return the name of the given member.""" + @staticmethod @abc.abstractstaticmethod def _add_file_to_archive(archive: ArchiveClass, member: ArchiveMember, - full_path: Text): + full_path: Text): """Add the file at full_path to the archive, via the given member.""" def get_meta(self) -> Dict[str, Union[str, dict]]: @@ -136,9 +146,7 @@ class ArchiveBasedAbstractParser(abstract.AbstractParser): # and keep them in the `items` variable. items = list() # type: List[ArchiveMember] - members = self._get_all_members(zin) - sort_key = lambda z: self._get_member_name(z) - for item in sorted(members, key=sort_key): + for item in sorted(self._get_all_members(zin), key=self._get_member_name): # Some fileformats do require to have the `mimetype` file # as the first file in the archive. if self._get_member_name(item) == 'mimetype': @@ -217,19 +225,21 @@ class TarParser(ArchiveBasedAbstractParser): @staticmethod def _clean_member(member: ArchiveMember) -> ArchiveMember: + assert isinstance(member, tarfile.TarInfo) # please mypy member.mtime = member.uid = member.gid = 0 member.uname = member.gname = '' return member @staticmethod def _get_member_meta(member: ArchiveMember) -> Dict[str, str]: + assert isinstance(member, tarfile.TarInfo) # please mypy metadata = {} if member.mtime != 0: - metadata['mtime'] = member.mtime + metadata['mtime'] = str(member.mtime) if member.uid != 0: - metadata['uid'] = member.uid + metadata['uid'] = str(member.uid) if member.gid != 0: - metadata['gid'] = member.gid + metadata['gid'] = str(member.gid) if member.uname != '': metadata['uname'] = member.uname if member.gname != '': @@ -238,15 +248,19 @@ class TarParser(ArchiveBasedAbstractParser): @staticmethod def _add_file_to_archive(archive: ArchiveClass, member: ArchiveMember, - full_path: Text): - archive.add(full_path, member.name, filter=TarParser._clean_member) + full_path: Text): + assert isinstance(member, tarfile.TarInfo) # please mypy + assert isinstance(archive, tarfile.TarFile) # please mypy + archive.add(full_path, member.name, filter=TarParser._clean_member) # type: ignore @staticmethod def _get_all_members(archive: ArchiveClass) -> List[ArchiveMember]: - return archive.getmembers() + assert isinstance(archive, tarfile.TarFile) # please mypy + return archive.getmembers() # type: ignore @staticmethod def _get_member_name(member: ArchiveMember) -> Text: + assert isinstance(member, tarfile.TarInfo) # please mypy return member.name class ZipParser(ArchiveBasedAbstractParser): @@ -264,6 +278,7 @@ class ZipParser(ArchiveBasedAbstractParser): @staticmethod def _clean_member(member: ArchiveMember) -> ArchiveMember: + assert isinstance(member, zipfile.ZipInfo) # please mypy member.create_system = 3 # Linux member.comment = b'' member.date_time = (1980, 1, 1, 0, 0, 0) # this is as early as a zipfile can be @@ -271,6 +286,7 @@ class ZipParser(ArchiveBasedAbstractParser): @staticmethod def _get_member_meta(member: ArchiveMember) -> Dict[str, str]: + assert isinstance(member, zipfile.ZipInfo) # please mypy metadata = {} if member.create_system == 3: # this is Linux pass @@ -289,14 +305,18 @@ class ZipParser(ArchiveBasedAbstractParser): @staticmethod def _add_file_to_archive(archive: ArchiveClass, member: ArchiveMember, - full_path: Text): + full_path: Text): + assert isinstance(archive, zipfile.ZipFile) # please mypy + assert isinstance(member, zipfile.ZipInfo) # please mypy with open(full_path, 'rb') as f: archive.writestr(member, f.read()) @staticmethod def _get_all_members(archive: ArchiveClass) -> List[ArchiveMember]: - return archive.infolist() + assert isinstance(archive, zipfile.ZipFile) # please mypy + return archive.infolist() # type: ignore @staticmethod def _get_member_name(member: ArchiveMember) -> Text: + assert isinstance(member, zipfile.ZipInfo) # please mypy return member.filename