Bump mypy typing coverage
This commit is contained in:
parent
b832a59414
commit
2ba38dd2a1
@ -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]
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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'
|
||||||
|
@ -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.
|
||||||
|
@ -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 = {}
|
||||||
|
@ -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
18
mat2
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user