Continue type annotations for utils.utils
This commit is contained in:
parent
ef88663ecd
commit
28a2e44bd3
4
mypy.ini
4
mypy.ini
|
|
@ -161,3 +161,7 @@ disallow_incomplete_defs = True
|
|||
[mypy-qutebrowser.utils.usertypes]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.utils.utils]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import socket
|
|||
import shlex
|
||||
import glob
|
||||
import mimetypes
|
||||
import typing
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtGui import QColor, QClipboard, QDesktopServices
|
||||
|
|
@ -88,7 +89,7 @@ class SelectionUnsupportedError(ClipboardError):
|
|||
|
||||
"""Raised if [gs]et_clipboard is used and selection=True is unsupported."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Primary selection is not supported on this "
|
||||
"platform!")
|
||||
|
||||
|
|
@ -98,7 +99,7 @@ class ClipboardEmptyError(ClipboardError):
|
|||
"""Raised if get_clipboard is used and the clipboard is empty."""
|
||||
|
||||
|
||||
def elide(text, length):
|
||||
def elide(text: str, length: int) -> str:
|
||||
"""Elide text so it uses a maximum of length chars."""
|
||||
if length < 1:
|
||||
raise ValueError("length must be >= 1!")
|
||||
|
|
@ -108,7 +109,7 @@ def elide(text, length):
|
|||
return text[:length - 1] + '\u2026'
|
||||
|
||||
|
||||
def elide_filename(filename, length):
|
||||
def elide_filename(filename: str, length: int) -> str:
|
||||
"""Elide a filename to the given length.
|
||||
|
||||
The difference to the elide() is that the text is removed from
|
||||
|
|
@ -140,7 +141,7 @@ def elide_filename(filename, length):
|
|||
return filename[:left] + elidestr + filename[-right:]
|
||||
|
||||
|
||||
def compact_text(text, elidelength=None):
|
||||
def compact_text(text: str, elidelength: int = None) -> str:
|
||||
"""Remove leading whitespace and newlines from a text and maybe elide it.
|
||||
|
||||
Args:
|
||||
|
|
@ -156,7 +157,7 @@ def compact_text(text, elidelength=None):
|
|||
return out
|
||||
|
||||
|
||||
def preload_resources():
|
||||
def preload_resources() -> None:
|
||||
"""Load resource files into the cache."""
|
||||
for subdir, pattern in [('html', '*.html'), ('javascript', '*.js')]:
|
||||
path = resource_filename(subdir)
|
||||
|
|
@ -165,7 +166,7 @@ def preload_resources():
|
|||
_resource_cache[sub_path] = read_file(sub_path)
|
||||
|
||||
|
||||
def read_file(filename, binary=False):
|
||||
def read_file(filename: str, binary: bool = False) -> typing.Union[str, bytes]:
|
||||
"""Get the contents of a file contained with qutebrowser.
|
||||
|
||||
Args:
|
||||
|
|
@ -187,19 +188,22 @@ def read_file(filename, binary=False):
|
|||
# https://github.com/pyinstaller/pyinstaller/wiki/FAQ#misc
|
||||
fn = os.path.join(os.path.dirname(sys.executable), filename)
|
||||
if binary:
|
||||
with open(fn, 'rb') as f:
|
||||
with open(fn, 'rb') as f: # type: typing.IO
|
||||
return f.read()
|
||||
else:
|
||||
with open(fn, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
else:
|
||||
data = pkg_resources.resource_string(qutebrowser.__name__, filename)
|
||||
if not binary:
|
||||
data = data.decode('UTF-8')
|
||||
return data
|
||||
data = pkg_resources.resource_string(
|
||||
qutebrowser.__name__, filename)
|
||||
|
||||
if binary:
|
||||
return data
|
||||
|
||||
return data.decode('UTF-8')
|
||||
|
||||
|
||||
def resource_filename(filename):
|
||||
def resource_filename(filename: str) -> str:
|
||||
"""Get the absolute filename of a file contained with qutebrowser.
|
||||
|
||||
Args:
|
||||
|
|
@ -213,7 +217,9 @@ def resource_filename(filename):
|
|||
return pkg_resources.resource_filename(qutebrowser.__name__, filename)
|
||||
|
||||
|
||||
def _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3, percent):
|
||||
def _get_color_percentage(a_c1: int, a_c2: int, a_c3:
|
||||
int, b_c1: int, b_c2: int, b_c3: int,
|
||||
percent: int) -> typing.Tuple[int, int, int]:
|
||||
"""Get a color which is percent% interpolated between start and end.
|
||||
|
||||
Args:
|
||||
|
|
@ -234,7 +240,12 @@ def _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3, percent):
|
|||
return (out_c1, out_c2, out_c3)
|
||||
|
||||
|
||||
def interpolate_color(start, end, percent, colorspace=QColor.Rgb):
|
||||
def interpolate_color(
|
||||
start: QColor,
|
||||
end: QColor,
|
||||
percent: int,
|
||||
colorspace: typing.Optional[QColor.Spec] = QColor.Rgb
|
||||
) -> QColor:
|
||||
"""Get an interpolated color value.
|
||||
|
||||
Args:
|
||||
|
|
@ -283,7 +294,7 @@ def interpolate_color(start, end, percent, colorspace=QColor.Rgb):
|
|||
return out
|
||||
|
||||
|
||||
def format_seconds(total_seconds):
|
||||
def format_seconds(total_seconds: int) -> str:
|
||||
"""Format a count of seconds to get a [H:]M:SS string."""
|
||||
prefix = '-' if total_seconds < 0 else ''
|
||||
hours, rem = divmod(abs(round(total_seconds)), 3600)
|
||||
|
|
@ -299,7 +310,9 @@ def format_seconds(total_seconds):
|
|||
return prefix + ':'.join(chunks)
|
||||
|
||||
|
||||
def format_size(size, base=1024, suffix=''):
|
||||
def format_size(size: typing.Optional[float],
|
||||
base: int = 1024,
|
||||
suffix: str = '') -> str:
|
||||
"""Format a byte size so it's human readable.
|
||||
|
||||
Inspired by http://stackoverflow.com/q/1094841
|
||||
|
|
@ -318,13 +331,13 @@ class FakeIOStream(io.TextIOBase):
|
|||
|
||||
"""A fake file-like stream which calls a function for write-calls."""
|
||||
|
||||
def __init__(self, write_func):
|
||||
def __init__(self, write_func: typing.Callable[[str], int]) -> None:
|
||||
super().__init__()
|
||||
self.write = write_func
|
||||
self.write = write_func # type: ignore
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fake_io(write_func):
|
||||
def fake_io(write_func: typing.Callable[[str], int]) -> typing.Iterator[None]:
|
||||
"""Run code with stdout and stderr replaced by FakeIOStreams.
|
||||
|
||||
Args:
|
||||
|
|
@ -334,21 +347,21 @@ def fake_io(write_func):
|
|||
old_stderr = sys.stderr
|
||||
fake_stderr = FakeIOStream(write_func)
|
||||
fake_stdout = FakeIOStream(write_func)
|
||||
sys.stderr = fake_stderr
|
||||
sys.stdout = fake_stdout
|
||||
sys.stderr = fake_stderr # type: ignore
|
||||
sys.stdout = fake_stdout # type: ignore
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
# If the code we did run did change sys.stdout/sys.stderr, we leave it
|
||||
# unchanged. Otherwise, we reset it.
|
||||
if sys.stdout is fake_stdout:
|
||||
if sys.stdout is fake_stdout: # type: ignore
|
||||
sys.stdout = old_stdout
|
||||
if sys.stderr is fake_stderr:
|
||||
if sys.stderr is fake_stderr: # type: ignore
|
||||
sys.stderr = old_stderr
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def disabled_excepthook():
|
||||
def disabled_excepthook() -> typing.Iterator[None]:
|
||||
"""Run code with the exception hook temporarily disabled."""
|
||||
old_excepthook = sys.excepthook
|
||||
sys.excepthook = sys.__excepthook__
|
||||
|
|
@ -381,7 +394,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
|
|||
_predicate: The condition which needs to be True to prevent exceptions
|
||||
"""
|
||||
|
||||
def __init__(self, retval, predicate=True):
|
||||
def __init__(self, retval: typing.Any, predicate: bool = True) -> None:
|
||||
"""Save decorator arguments.
|
||||
|
||||
Gets called on parse-time with the decorator arguments.
|
||||
|
|
@ -392,7 +405,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
|
|||
self._retval = retval
|
||||
self._predicate = predicate
|
||||
|
||||
def __call__(self, func):
|
||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
||||
"""Called when a function should be decorated.
|
||||
|
||||
Args:
|
||||
|
|
@ -407,7 +420,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
|
|||
retval = self._retval
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
def wrapper(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
||||
"""Call the original function."""
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
|
|
@ -418,7 +431,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
|
|||
return wrapper
|
||||
|
||||
|
||||
def is_enum(obj):
|
||||
def is_enum(obj: typing.Any) -> bool:
|
||||
"""Check if a given object is an enum."""
|
||||
try:
|
||||
return issubclass(obj, enum.Enum)
|
||||
|
|
@ -426,7 +439,9 @@ def is_enum(obj):
|
|||
return False
|
||||
|
||||
|
||||
def get_repr(obj, constructor=False, **attrs):
|
||||
def get_repr(obj: typing.Any,
|
||||
constructor: bool = False,
|
||||
**attrs: typing.Mapping) -> str:
|
||||
"""Get a suitable __repr__ string for an object.
|
||||
|
||||
Args:
|
||||
|
|
@ -449,7 +464,7 @@ def get_repr(obj, constructor=False, **attrs):
|
|||
return '<{}>'.format(cls)
|
||||
|
||||
|
||||
def qualname(obj):
|
||||
def qualname(obj: typing.Any) -> str:
|
||||
"""Get the fully qualified name of an object.
|
||||
|
||||
Based on twisted.python.reflect.fullyQualifiedName.
|
||||
|
|
@ -477,7 +492,9 @@ def qualname(obj):
|
|||
return repr(obj)
|
||||
|
||||
|
||||
def raises(exc, func, *args):
|
||||
def raises(exc: typing.Union[BaseException, typing.Tuple[BaseException]],
|
||||
func: typing.Callable,
|
||||
*args: typing.Any) -> bool:
|
||||
"""Check if a function raises a given exception.
|
||||
|
||||
Args:
|
||||
|
|
@ -490,13 +507,13 @@ def raises(exc, func, *args):
|
|||
"""
|
||||
try:
|
||||
func(*args)
|
||||
except exc:
|
||||
except exc: # type: ignore
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def force_encoding(text, encoding):
|
||||
def force_encoding(text: str, encoding: str) -> str:
|
||||
"""Make sure a given text is encodable with the given encoding.
|
||||
|
||||
This replaces all chars not encodable with question marks.
|
||||
|
|
@ -504,7 +521,8 @@ def force_encoding(text, encoding):
|
|||
return text.encode(encoding, errors='replace').decode(encoding)
|
||||
|
||||
|
||||
def sanitize_filename(name, replacement='_'):
|
||||
def sanitize_filename(name: str,
|
||||
replacement: typing.Optional[str] = '_') -> str:
|
||||
"""Replace invalid filename characters.
|
||||
|
||||
Note: This should be used for the basename, as it also removes the path
|
||||
|
|
@ -537,7 +555,7 @@ def sanitize_filename(name, replacement='_'):
|
|||
return name
|
||||
|
||||
|
||||
def set_clipboard(data, selection=False):
|
||||
def set_clipboard(data: str, selection: bool = False) -> None:
|
||||
"""Set the clipboard to some given data."""
|
||||
global fake_clipboard
|
||||
if selection and not supports_selection():
|
||||
|
|
@ -551,7 +569,7 @@ def set_clipboard(data, selection=False):
|
|||
QApplication.clipboard().setText(data, mode=mode)
|
||||
|
||||
|
||||
def get_clipboard(selection=False, fallback=False):
|
||||
def get_clipboard(selection: bool = False, fallback: bool = False) -> str:
|
||||
"""Get data from the clipboard.
|
||||
|
||||
Args:
|
||||
|
|
@ -584,12 +602,12 @@ def get_clipboard(selection=False, fallback=False):
|
|||
return data
|
||||
|
||||
|
||||
def supports_selection():
|
||||
def supports_selection() -> bool:
|
||||
"""Check if the OS supports primary selection."""
|
||||
return QApplication.clipboard().supportsSelection()
|
||||
|
||||
|
||||
def random_port():
|
||||
def random_port() -> int:
|
||||
"""Get a random free port."""
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.bind(('localhost', 0))
|
||||
|
|
@ -598,7 +616,7 @@ def random_port():
|
|||
return port
|
||||
|
||||
|
||||
def open_file(filename, cmdline=None):
|
||||
def open_file(filename: str, cmdline: str = None) -> None:
|
||||
"""Open the given file.
|
||||
|
||||
If cmdline is not given, downloads.open_dispatcher is used.
|
||||
|
|
@ -634,6 +652,8 @@ def open_file(filename, cmdline=None):
|
|||
if cmdline is None and override:
|
||||
cmdline = override
|
||||
|
||||
assert cmdline is not None
|
||||
|
||||
cmd, *args = shlex.split(cmdline)
|
||||
args = [arg.replace('{}', filename) for arg in args]
|
||||
if '{}' not in cmdline:
|
||||
|
|
@ -644,11 +664,11 @@ def open_file(filename, cmdline=None):
|
|||
proc.start_detached(cmd, args)
|
||||
|
||||
|
||||
def unused(_arg):
|
||||
def unused(_arg: typing.Any) -> None:
|
||||
"""Function which does nothing to avoid pylint complaining."""
|
||||
|
||||
|
||||
def expand_windows_drive(path):
|
||||
def expand_windows_drive(path: str) -> str:
|
||||
r"""Expand a drive-path like E: into E:\.
|
||||
|
||||
Does nothing for other paths.
|
||||
|
|
@ -666,7 +686,7 @@ def expand_windows_drive(path):
|
|||
return path
|
||||
|
||||
|
||||
def yaml_load(f):
|
||||
def yaml_load(f: typing.Union[str, typing.IO[str]]) -> typing.Any:
|
||||
"""Wrapper over yaml.load using the C loader if possible."""
|
||||
start = datetime.datetime.now()
|
||||
|
||||
|
|
@ -696,7 +716,8 @@ def yaml_load(f):
|
|||
return data
|
||||
|
||||
|
||||
def yaml_dump(data, f=None):
|
||||
def yaml_dump(data: typing.Any,
|
||||
f: typing.IO[str] = None) -> typing.Optional[str]:
|
||||
"""Wrapper over yaml.dump using the C dumper if possible.
|
||||
|
||||
Also returns a str instead of bytes.
|
||||
|
|
@ -709,7 +730,7 @@ def yaml_dump(data, f=None):
|
|||
return yaml_data.decode('utf-8')
|
||||
|
||||
|
||||
def chunk(elems, n):
|
||||
def chunk(elems: typing.Sequence, n: int) -> typing.Iterator[typing.Sequence]:
|
||||
"""Yield successive n-sized chunks from elems.
|
||||
|
||||
If elems % n != 0, the last chunk will be smaller.
|
||||
|
|
@ -720,7 +741,7 @@ def chunk(elems, n):
|
|||
yield elems[i:i + n]
|
||||
|
||||
|
||||
def guess_mimetype(filename, fallback=False):
|
||||
def guess_mimetype(filename: str, fallback: bool = False) -> str:
|
||||
"""Guess a mimetype based on a filename.
|
||||
|
||||
Args:
|
||||
|
|
@ -736,7 +757,7 @@ def guess_mimetype(filename, fallback=False):
|
|||
return mimetype
|
||||
|
||||
|
||||
def ceil_log(number, base):
|
||||
def ceil_log(number: int, base: int) -> int:
|
||||
"""Compute max(1, ceil(log(number, base))).
|
||||
|
||||
Use only integer arithmetic in order to avoid numerical error.
|
||||
|
|
|
|||
Loading…
Reference in New Issue