1
0
Fork 0

Bump mypy typing coverage

This commit is contained in:
jvoisin 2018-10-12 11:58:01 +02:00
parent b832a59414
commit 2ba38dd2a1
10 changed files with 52 additions and 41 deletions

View File

@ -8,6 +8,7 @@ from typing import Dict, Optional
# make pyflakes happy # make pyflakes happy
assert Dict assert Dict
assert Optional
# A set of extension that aren't supported, despite matching a supported mimetype # A set of extension that aren't supported, despite matching a supported mimetype
UNSUPPORTED_EXTENSIONS = { UNSUPPORTED_EXTENSIONS = {
@ -36,7 +37,7 @@ DEPENDENCIES = {
'mutagen': 'Mutagen', 'mutagen': 'Mutagen',
} }
def _get_exiftool_path() -> Optional[str]: # pragma: no cover def _get_exiftool_path() -> str: # pragma: no cover
exiftool_path = '/usr/bin/exiftool' exiftool_path = '/usr/bin/exiftool'
if os.path.isfile(exiftool_path): if os.path.isfile(exiftool_path):
if os.access(exiftool_path, os.X_OK): if os.access(exiftool_path, os.X_OK):
@ -48,7 +49,7 @@ def _get_exiftool_path() -> Optional[str]: # pragma: no cover
if os.access(exiftool_path, os.X_OK): if os.access(exiftool_path, os.X_OK):
return exiftool_path return exiftool_path
return None raise ValueError
def check_dependencies() -> dict: def check_dependencies() -> dict:
ret = collections.defaultdict(bool) # type: Dict[str, bool] ret = collections.defaultdict(bool) # type: Dict[str, bool]

View File

@ -1,6 +1,6 @@
import abc import abc
import os import os
from typing import Set, Dict from typing import Set, Dict, Union
assert Set # make pyflakes happy assert Set # make pyflakes happy
@ -22,7 +22,7 @@ class AbstractParser(abc.ABC):
self.lightweight_cleaning = False self.lightweight_cleaning = False
@abc.abstractmethod @abc.abstractmethod
def get_meta(self) -> Dict[str, str]: def get_meta(self) -> Dict[str, Union[str, dict]]:
pass # pragma: no cover pass # pragma: no cover
@abc.abstractmethod @abc.abstractmethod

View File

@ -4,13 +4,14 @@ import tempfile
import os import os
import logging import logging
import shutil import shutil
from typing import Dict, Set, Pattern from typing import Dict, Set, Pattern, Union
from . import abstract, UnknownMemberPolicy, parser_factory from . import abstract, UnknownMemberPolicy, parser_factory
# Make pyflakes happy # Make pyflakes happy
assert Set assert Set
assert Pattern assert Pattern
assert Union
class ArchiveBasedAbstractParser(abstract.AbstractParser): class ArchiveBasedAbstractParser(abstract.AbstractParser):

View File

@ -2,6 +2,7 @@ import mimetypes
import os import os
import shutil import shutil
import tempfile import tempfile
from typing import Dict, Union
import mutagen import mutagen
@ -16,13 +17,13 @@ class MutagenParser(abstract.AbstractParser):
except mutagen.MutagenError: except mutagen.MutagenError:
raise ValueError raise ValueError
def get_meta(self): def get_meta(self) -> Dict[str, Union[str, dict]]:
f = mutagen.File(self.filename) f = mutagen.File(self.filename)
if f.tags: if f.tags:
return {k:', '.join(v) for k, v in f.tags.items()} return {k:', '.join(v) for k, v in f.tags.items()}
return {} return {}
def remove_all(self): def remove_all(self) -> bool:
shutil.copy(self.filename, self.output_filename) shutil.copy(self.filename, self.output_filename)
f = mutagen.File(self.output_filename) f = mutagen.File(self.output_filename)
f.delete() f.delete()
@ -33,8 +34,8 @@ class MutagenParser(abstract.AbstractParser):
class MP3Parser(MutagenParser): class MP3Parser(MutagenParser):
mimetypes = {'audio/mpeg', } mimetypes = {'audio/mpeg', }
def get_meta(self): def get_meta(self) -> Dict[str, Union[str, dict]]:
metadata = {} metadata = {} # type: Dict[str, Union[str, dict]]
meta = mutagen.File(self.filename).tags meta = mutagen.File(self.filename).tags
for key in meta: for key in meta:
metadata[key.rstrip(' \t\r\n\0')] = ', '.join(map(str, meta[key].text)) metadata[key.rstrip(' \t\r\n\0')] = ', '.join(map(str, meta[key].text))
@ -48,7 +49,7 @@ class OGGParser(MutagenParser):
class FLACParser(MutagenParser): class FLACParser(MutagenParser):
mimetypes = {'audio/flac', 'audio/x-flac'} mimetypes = {'audio/flac', 'audio/x-flac'}
def remove_all(self): def remove_all(self) -> bool:
shutil.copy(self.filename, self.output_filename) shutil.copy(self.filename, self.output_filename)
f = mutagen.File(self.output_filename) f = mutagen.File(self.output_filename)
f.clear_pictures() f.clear_pictures()
@ -56,16 +57,21 @@ class FLACParser(MutagenParser):
f.save(deleteid3=True) f.save(deleteid3=True)
return True return True
def get_meta(self): def get_meta(self) -> Dict[str, Union[str, dict]]:
meta = super().get_meta() meta = super().get_meta()
for num, picture in enumerate(mutagen.File(self.filename).pictures): for num, picture in enumerate(mutagen.File(self.filename).pictures):
name = picture.desc if picture.desc else 'Cover %d' % num name = picture.desc if picture.desc else 'Cover %d' % num
extension = mimetypes.guess_extension(picture.mime)
if extension is None: # pragma: no cover
meta[name] = 'harmful data'
continue
_, fname = tempfile.mkstemp() _, fname = tempfile.mkstemp()
fname = fname + extension
with open(fname, 'wb') as f: with open(fname, 'wb') as f:
f.write(picture.data) f.write(picture.data)
extension = mimetypes.guess_extension(picture.mime) p, _ = parser_factory.get_parser(fname) # type: ignore
shutil.move(fname, fname + extension) # Mypy chokes on ternaries :/
p, _ = parser_factory.get_parser(fname+extension) meta[name] = p.get_meta() if p else 'harmful data' # type: ignore
meta[name] = p.get_meta() if p else 'harmful data' os.remove(fname)
os.remove(fname + extension)
return meta return meta

View File

@ -1,5 +1,5 @@
import shutil import shutil
from typing import Dict from typing import Dict, Union
from . import abstract from . import abstract
@ -7,7 +7,7 @@ class HarmlessParser(abstract.AbstractParser):
""" This is the parser for filetypes that can not contain metadata. """ """ This is the parser for filetypes that can not contain metadata. """
mimetypes = {'text/plain', 'image/x-ms-bmp'} mimetypes = {'text/plain', 'image/x-ms-bmp'}
def get_meta(self) -> Dict[str, str]: def get_meta(self) -> Dict[str, Union[str, dict]]:
return dict() return dict()
def remove_all(self) -> bool: def remove_all(self) -> bool:

View File

@ -5,7 +5,7 @@ import os
import shutil import shutil
import tempfile import tempfile
import re import re
from typing import Set from typing import Set, Dict, Union
import cairo import cairo
@ -25,7 +25,7 @@ class _ImageParser(abstract.AbstractParser):
meta_whitelist = set() # type: Set[str] meta_whitelist = set() # type: Set[str]
@staticmethod @staticmethod
def __handle_problematic_filename(filename: str, callback) -> str: def __handle_problematic_filename(filename: str, callback) -> bytes:
""" This method takes a filename with a problematic name, """ This method takes a filename with a problematic name,
and safely applies it a `callback`.""" and safely applies it a `callback`."""
tmpdirname = tempfile.mkdtemp() tmpdirname = tempfile.mkdtemp()
@ -35,7 +35,7 @@ class _ImageParser(abstract.AbstractParser):
shutil.rmtree(tmpdirname) shutil.rmtree(tmpdirname)
return out return out
def get_meta(self): def get_meta(self) -> Dict[str, Union[str, dict]]:
""" There is no way to escape the leading(s) dash(es) of the current """ There is no way to escape the leading(s) dash(es) of the current
self.filename to prevent parameter injections, so we need to take care self.filename to prevent parameter injections, so we need to take care
of this. of this.
@ -71,7 +71,7 @@ class PNGParser(_ImageParser):
except MemoryError: # pragma: no cover except MemoryError: # pragma: no cover
raise ValueError raise ValueError
def remove_all(self): def remove_all(self) -> bool:
surface = cairo.ImageSurface.create_from_png(self.filename) surface = cairo.ImageSurface.create_from_png(self.filename)
surface.write_to_png(self.output_filename) surface.write_to_png(self.output_filename)
return True return True
@ -83,7 +83,12 @@ class GdkPixbufAbstractParser(_ImageParser):
""" """
_type = '' _type = ''
def remove_all(self): def __init__(self, filename):
super().__init__(filename)
if imghdr.what(filename) != self._type: # better safe than sorry
raise ValueError
def remove_all(self) -> bool:
_, extension = os.path.splitext(self.filename) _, extension = os.path.splitext(self.filename)
pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.filename) pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.filename)
if extension.lower() == '.jpg': if extension.lower() == '.jpg':
@ -91,11 +96,6 @@ class GdkPixbufAbstractParser(_ImageParser):
pixbuf.savev(self.output_filename, extension[1:], [], []) pixbuf.savev(self.output_filename, extension[1:], [], [])
return True return True
def __init__(self, filename):
super().__init__(filename)
if imghdr.what(filename) != self._type: # better safe than sorry
raise ValueError
class JPGParser(GdkPixbufAbstractParser): class JPGParser(GdkPixbufAbstractParser):
_type = 'jpeg' _type = 'jpeg'

View File

@ -2,7 +2,7 @@ import logging
import os import os
import re import re
import zipfile import zipfile
from typing import Dict, Set, Pattern, Tuple from typing import Dict, Set, Pattern, Tuple, Union
import xml.etree.ElementTree as ET # type: ignore import xml.etree.ElementTree as ET # type: ignore
@ -296,7 +296,7 @@ class MSOfficeParser(ArchiveBasedAbstractParser):
return True return True
def get_meta(self) -> Dict[str, str]: def get_meta(self) -> Dict[str, Union[str, dict]]:
""" """
Yes, I know that parsing xml with regexp ain't pretty, Yes, I know that parsing xml with regexp ain't pretty,
be my guest and fix it if you want. be my guest and fix it if you want.
@ -381,7 +381,7 @@ class LibreOfficeParser(ArchiveBasedAbstractParser):
return False return False
return True return True
def get_meta(self) -> Dict[str, str]: def get_meta(self) -> Dict[str, Union[str, dict]]:
""" """
Yes, I know that parsing xml with regexp ain't pretty, Yes, I know that parsing xml with regexp ain't pretty,
be my guest and fix it if you want. be my guest and fix it if you want.

View File

@ -7,6 +7,7 @@ import re
import logging import logging
import tempfile import tempfile
import io import io
from typing import Dict, Union
from distutils.version import LooseVersion from distutils.version import LooseVersion
import cairo import cairo
@ -130,7 +131,7 @@ class PDFParser(abstract.AbstractParser):
metadata[key] = value metadata[key] = value
return metadata return metadata
def get_meta(self): def get_meta(self) -> Dict[str, Union[str, dict]]:
""" Return a dict with all the meta of the file """ Return a dict with all the meta of the file
""" """
metadata = {} metadata = {}

View File

@ -14,7 +14,7 @@ class TorrentParser(abstract.AbstractParser):
if self.dict_repr is None: if self.dict_repr is None:
raise ValueError raise ValueError
def get_meta(self) -> Dict[str, str]: def get_meta(self) -> Dict[str, Union[str, dict]]:
metadata = {} metadata = {}
for key, value in self.dict_repr.items(): for key, value in self.dict_repr.items():
if key not in self.whitelist: if key not in self.whitelist:

18
mat2
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
from typing import Tuple, Generator, List from typing import Tuple, Generator, List, Union
import sys import sys
import mimetypes import mimetypes
import argparse import argparse
@ -18,6 +18,7 @@ __version__ = '0.4.0'
# Make pyflakes happy # Make pyflakes happy
assert Tuple assert Tuple
assert Union
def __check_file(filename: str, mode: int=os.R_OK) -> bool: def __check_file(filename: str, mode: int=os.R_OK) -> bool:
@ -98,12 +99,12 @@ def clean_meta(filename: str, is_lightweight: bool, policy: UnknownMemberPolicy)
return p.remove_all() return p.remove_all()
def show_parsers(): def show_parsers() -> bool:
print('[+] Supported formats:') print('[+] Supported formats:')
formats = set() formats = set() # Set[str]
for parser in parser_factory._get_parsers(): for parser in parser_factory._get_parsers(): # type: ignore
for mtype in parser.mimetypes: for mtype in parser.mimetypes:
extensions = set() extensions = set() # Set[str]
for extension in mimetypes.guess_all_extensions(mtype): for extension in mimetypes.guess_all_extensions(mtype):
if extension not in UNSUPPORTED_EXTENSIONS: if extension not in UNSUPPORTED_EXTENSIONS:
extensions.add(extension) extensions.add(extension)
@ -113,6 +114,7 @@ def show_parsers():
continue continue
formats.add(' - %s (%s)' % (mtype, ', '.join(extensions))) formats.add(' - %s (%s)' % (mtype, ', '.join(extensions)))
print('\n'.join(sorted(formats))) print('\n'.join(sorted(formats)))
return True
def __get_files_recursively(files: List[str]) -> Generator[str, None, None]: def __get_files_recursively(files: List[str]) -> Generator[str, None, None]:
@ -126,7 +128,7 @@ def __get_files_recursively(files: List[str]) -> Generator[str, None, None]:
elif __check_file(f): elif __check_file(f):
yield f yield f
def main(): def main() -> int:
arg_parser = create_arg_parser() arg_parser = create_arg_parser()
args = arg_parser.parse_args() args = arg_parser.parse_args()
@ -135,13 +137,13 @@ def main():
if not args.files: if not args.files:
if args.list: if args.list:
show_parsers() return show_parsers()
elif args.check_dependencies: elif args.check_dependencies:
print("Dependencies required for MAT2 %s:" % __version__) print("Dependencies required for MAT2 %s:" % __version__)
for key, value in sorted(check_dependencies().items()): for key, value in sorted(check_dependencies().items()):
print('- %s: %s' % (key, 'yes' if value else 'no')) print('- %s: %s' % (key, 'yes' if value else 'no'))
else: else:
return arg_parser.print_help() arg_parser.print_help()
return 0 return 0
elif args.show: elif args.show: