mypy: Set disallow_any_generics

See #6100
This commit is contained in:
Florian Bruhin 2021-05-19 13:15:09 +02:00
parent 06e49efd3d
commit c01b47f27b
25 changed files with 125 additions and 69 deletions

View File

@ -3,7 +3,7 @@ python_version = 3.6
### --strict
warn_unused_configs = True
# disallow_any_generics = True
disallow_any_generics = True
disallow_subclassing_any = True
# disallow_untyped_calls = True
# disallow_untyped_defs = True
@ -83,6 +83,10 @@ disallow_untyped_defs = True
[mypy-qutebrowser.config.*]
disallow_untyped_defs = True
[mypy-qutebrowser.config.configtypes]
# Needs some major work to use specific generics
disallow_any_generics = False
[mypy-qutebrowser.api.*]
disallow_untyped_defs = True

View File

@ -105,6 +105,9 @@ def check_exclusive(flags: Iterable[bool], names: Iterable[str]) -> None:
raise CommandError("Only one of {} can be given!".format(argstr))
_CmdHandlerType = Callable[..., Any]
class register: # noqa: N801,N806 pylint: disable=invalid-name
"""Decorator to register a new command handler."""
@ -130,7 +133,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name
# The arguments to pass to Command.
self._kwargs = kwargs
def __call__(self, func: Callable) -> Callable:
def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
"""Register the command before running the function.
Gets called when a function should be decorated.
@ -222,7 +225,7 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name
self._argname = argname # The name of the argument to handle.
self._kwargs = kwargs # Valid ArgInfo members.
def __call__(self, func: Callable) -> Callable:
def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
funcname = func.__name__
if self._argname not in inspect.signature(func).parameters:

View File

@ -22,13 +22,13 @@
"""Hooks for extensions."""
import importlib
from typing import Callable
from typing import Callable, Any
from qutebrowser.extensions import loader
def _add_module_info(func: Callable) -> loader.ModuleInfo:
def _add_module_info(func: Callable[..., Any]) -> loader.ModuleInfo:
"""Add module info to the given function."""
module = importlib.import_module(func.__module__)
return loader.add_module_info(module)
@ -48,7 +48,7 @@ class init:
message.info("Extension initialized.")
"""
def __call__(self, func: Callable) -> Callable:
def __call__(self, func: loader.InitHookType) -> loader.InitHookType:
info = _add_module_info(func)
if info.init_hook is not None:
raise ValueError("init hook is already registered!")
@ -86,7 +86,10 @@ class config_changed:
def __init__(self, option_filter: str = None) -> None:
self._filter = option_filter
def __call__(self, func: Callable) -> Callable:
def __call__(
self,
func: loader.ConfigChangedHookType,
) -> loader.ConfigChangedHookType:
info = _add_module_info(func)
info.config_changed_hooks.append((self._filter, func))
return func

View File

@ -34,9 +34,10 @@ from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
from PyQt5.QtNetwork import QNetworkAccessManager
if TYPE_CHECKING:
from PyQt5.QtWebKit import QWebHistory
from PyQt5.QtWebKit import QWebHistory, QWebHistoryItem
from PyQt5.QtWebKitWidgets import QWebPage
from PyQt5.QtWebEngineWidgets import QWebEngineHistory, QWebEnginePage
from PyQt5.QtWebEngineWidgets import (
QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage)
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
@ -634,8 +635,8 @@ class AbstractHistoryPrivate:
"""Deserialize from a format produced by self.serialize."""
raise NotImplementedError
def load_items(self, items: Sequence) -> None:
"""Deserialize from a list of WebHistoryItems."""
def load_items(self, items: Sequence[sessions.TabHistoryItem]) -> None:
"""Deserialize from a list of TabHistoryItems."""
raise NotImplementedError
@ -651,7 +652,7 @@ class AbstractHistory:
def __len__(self) -> int:
raise NotImplementedError
def __iter__(self) -> Iterable:
def __iter__(self) -> Iterable[Union['QWebHistoryItem', 'QWebEngineHistoryItem']]:
raise NotImplementedError
def _check_count(self, count: int) -> None:

View File

@ -602,7 +602,7 @@ class CommandDispatcher:
widget = self._current_widget()
url = self._current_url()
handlers: Dict[str, Callable] = {
handlers: Dict[str, Callable[..., QUrl]] = {
'prev': functools.partial(navigate.prevnext, prev=True),
'next': functools.partial(navigate.prevnext, prev=False),
'up': navigate.path_up,

View File

@ -92,7 +92,8 @@ class Redirect(Exception):
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
_HandlerRet = Tuple[str, Union[str, bytes]]
_Handler = TypeVar('_Handler', bound=Callable[[QUrl], _HandlerRet])
_HandlerCallable = Callable[[QUrl], _HandlerRet]
_Handler = TypeVar('_Handler', bound=_HandlerCallable)
class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
@ -105,7 +106,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
def __init__(self, name: str) -> None:
self._name = name
self._function: Optional[Callable] = None
self._function: Optional[_HandlerCallable] = None
def __call__(self, function: _Handler) -> _Handler:
self._function = function

View File

@ -58,7 +58,7 @@ def css_selector(group: str, url: QUrl) -> str:
return ','.join(selectors[group])
class AbstractWebElement(collections.abc.MutableMapping):
class AbstractWebElement(collections.abc.MutableMapping[str, str]):
"""A wrapper around QtWebKit/QtWebEngine web element."""

View File

@ -35,10 +35,10 @@ HISTORY_STREAM_VERSION = 3
def _serialize_item(item, stream):
"""Serialize a single WebHistoryItem into a QDataStream.
"""Serialize a single TabHistoryItem into a QDataStream.
Args:
item: The WebHistoryItem to write.
item: The TabHistoryItem to write.
stream: The QDataStream to write to.
"""
# Thanks to Otter Browser:
@ -108,10 +108,10 @@ def _serialize_item(item, stream):
def serialize(items):
"""Serialize a list of WebHistoryItems to a data stream.
"""Serialize a list of TabHistoryItems to a data stream.
Args:
items: An iterable of WebHistoryItems.
items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.

View File

@ -64,10 +64,10 @@ def _serialize_item(item):
def serialize(items):
"""Serialize a list of WebHistoryItems to a data stream.
"""Serialize a list of TabHistoryItems to a data stream.
Args:
items: An iterable of WebHistoryItems.
items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.

View File

@ -97,7 +97,10 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
else:
return False
def __call__(self, func: Callable) -> Callable:
def __call__(
self,
func: Callable[..., None],
) -> Callable[..., None]:
"""Filter calls to the decorated function.
Gets called when a function should be decorated.
@ -105,7 +108,9 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
Adds a filter which returns if we're not interested in the change-event
and calls the wrapped function if we are.
We assume the function passed doesn't take any parameters.
We assume the function passed doesn't take any parameters. However, it
could take a "self" argument, so we can't cleary express this in the
type above.
Args:
func: The function to be decorated.
@ -309,7 +314,7 @@ class Config(QObject):
def _init_values(self) -> None:
"""Populate the self._values dict."""
self._values: Mapping = {}
self._values: Mapping[str, configutils.Values] = {}
for name, opt in configdata.DATA.items():
self._values[name] = configutils.Values(opt)

View File

@ -21,7 +21,7 @@
import os.path
import contextlib
from typing import TYPE_CHECKING, Iterator, List, Optional
from typing import TYPE_CHECKING, Iterator, List, Optional, Any, Tuple
from PyQt5.QtCore import QUrl
@ -475,7 +475,7 @@ class ConfigCommands:
raise cmdutils.CommandError("{} already exists - use --force to "
"overwrite!".format(filename))
options: List = []
options: List[Tuple[Optional[urlmatch.UrlPattern], configdata.Option, Any]] = []
if defaults:
options = [(None, opt, opt.default)
for _name, opt in sorted(configdata.DATA.items())]

View File

@ -30,7 +30,7 @@ import configparser
import contextlib
import re
from typing import (TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Mapping,
MutableMapping, Optional, cast)
MutableMapping, Optional, Tuple, cast)
import yaml
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QSettings, qVersion
@ -302,18 +302,18 @@ class YamlConfig(QObject):
self._validate_names(settings)
self._build_values(settings)
def _load_settings_object(self, yaml_data: Any) -> '_SettingsType':
def _load_settings_object(self, yaml_data: Any) -> _SettingsType:
"""Load the settings from the settings: key."""
return self._pop_object(yaml_data, 'settings', dict)
def _load_legacy_settings_object(self, yaml_data: Any) -> '_SettingsType':
def _load_legacy_settings_object(self, yaml_data: Any) -> _SettingsType:
data = self._pop_object(yaml_data, 'global', dict)
settings = {}
for name, value in data.items():
settings[name] = {'global': value}
return settings
def _build_values(self, settings: Mapping) -> None:
def _build_values(self, settings: Mapping[str, Any]) -> None:
"""Build up self._values from the values in the given dict."""
errors = []
for name, yaml_values in settings.items():
@ -740,9 +740,17 @@ class ConfigPyWriter:
def __init__(
self,
options: List,
options: List[
Tuple[
Optional[urlmatch.UrlPattern],
configdata.Option,
Any
]
],
bindings: MutableMapping[str, Mapping[str, Optional[str]]],
*, commented: bool) -> None:
*,
commented: bool,
) -> None:
self._options = options
self._bindings = bindings
self._commented = commented

View File

@ -85,7 +85,11 @@ class AttributeInfo:
"""Info about a settings attribute."""
def __init__(self, *attributes: Any, converter: Callable = None) -> None:
def __init__(
self,
*attributes: Any,
converter: Callable[[Any], bool] = None,
) -> None:
self.attributes = attributes
if converter is None:
self.converter = lambda val: val

View File

@ -39,6 +39,9 @@ from qutebrowser.misc import objects
# ModuleInfo objects for all loaded plugins
_module_infos = []
InitHookType = Callable[['InitContext'], None]
ConfigChangedHookType = Callable[[], None]
@dataclasses.dataclass
class InitContext:
@ -59,9 +62,13 @@ class ModuleInfo:
"""
skip_hooks: bool = False
init_hook: Optional[Callable] = None
config_changed_hooks: List[Tuple[Optional[str], Callable]] = dataclasses.field(
default_factory=list)
init_hook: Optional[InitHookType] = None
config_changed_hooks: List[
Tuple[
Optional[str],
ConfigChangedHookType,
]
] = dataclasses.field(default_factory=list)
@dataclasses.dataclass

View File

@ -112,7 +112,7 @@ class BindingTrie:
return lines
def update(self, mapping: Mapping) -> None:
def update(self, mapping: Mapping[keyutils.KeySequence, str]) -> None:
"""Add data from the given mapping to the trie."""
for key in mapping:
self[key] = mapping[key]

View File

@ -458,7 +458,7 @@ class KeySequence:
assert self
self._validate()
def _convert_key(self, key: Qt.Key) -> int:
def _convert_key(self, key: Union[int, Qt.KeyboardModifiers]) -> int:
"""Convert a single key for QKeySequence."""
assert isinstance(key, (int, Qt.KeyboardModifiers)), key
return int(key)

View File

@ -30,7 +30,7 @@ from typing import Any, Callable, List, Optional, Tuple, TypeVar
_CACHE_FUNCTIONS: List[Tuple[str, Any]] = []
_T = TypeVar('_T', bound=Callable)
_T = TypeVar('_T', bound=Callable[..., Any])
def register(name: Optional[str] = None) -> Callable[[_T], _T]:

View File

@ -65,7 +65,7 @@ import re
import dataclasses
import mmap
import pathlib
from typing import IO, ClassVar, Dict, Optional, Tuple, cast
from typing import Any, IO, ClassVar, Dict, Optional, Tuple, cast
from PyQt5.QtCore import QLibraryInfo
@ -93,7 +93,7 @@ class Endianness(enum.Enum):
big = 2
def _unpack(fmt: str, fobj: IO[bytes]) -> Tuple:
def _unpack(fmt: str, fobj: IO[bytes]) -> Tuple[Any, ...]:
"""Unpack the given struct format from the given file."""
size = struct.calcsize(fmt)
data = _safe_read(fobj, size)

View File

@ -45,7 +45,7 @@ class Throttle(QObject):
"""
def __init__(self,
func: Callable,
func: Callable[..., None],
delay_ms: int,
parent: QObject = None) -> None:
"""Constructor.

View File

@ -35,7 +35,7 @@ from qutebrowser.misc import objects
from qutebrowser.qt import sip
def log_events(klass: Type) -> Type:
def log_events(klass: Type[QObject]) -> Type[QObject]:
"""Class decorator to log Qt events."""
old_event = klass.event
@ -46,7 +46,7 @@ def log_events(klass: Type) -> Type:
qenum_key(QEvent, e.type())))
return old_event(self, e)
klass.event = new_event
klass.event = new_event # type: ignore[assignment]
return klass
@ -96,10 +96,13 @@ def log_signals(obj: QObject) -> QObject:
return obj
def qenum_key(base: Type,
value: Union[int, sip.simplewrapper],
_EnumValueType = Union[sip.simplewrapper, int]
def qenum_key(base: Type[_EnumValueType],
value: _EnumValueType,
add_base: bool = False,
klass: Type = None) -> str:
klass: Type[_EnumValueType] = None) -> str:
"""Convert a Qt Enum value to its key as a string.
Args:
@ -119,8 +122,9 @@ def qenum_key(base: Type,
raise TypeError("Can't guess enum class of an int!")
try:
idx = base.staticMetaObject.indexOfEnumerator(klass.__name__)
meta_enum = base.staticMetaObject.enumerator(idx)
meta_obj = base.staticMetaObject # type: ignore[union-attr]
idx = meta_obj.indexOfEnumerator(klass.__name__)
meta_enum = meta_obj.enumerator(idx)
ret = meta_enum.valueToKey(int(value)) # type: ignore[arg-type]
except AttributeError:
ret = None
@ -139,10 +143,10 @@ def qenum_key(base: Type,
return ret
def qflags_key(base: Type,
value: Union[int, sip.simplewrapper],
def qflags_key(base: Type[_EnumValueType],
value: _EnumValueType,
add_base: bool = False,
klass: Type = None) -> str:
klass: Type[_EnumValueType] = None) -> str:
"""Convert a Qt QFlags value to its keys as string.
Note: Passing a combined value (such as Qt.AlignCenter) will get the names
@ -220,7 +224,7 @@ def signal_name(sig: pyqtBoundSignal) -> str:
return m.group('name')
def format_args(args: Sequence = None, kwargs: Mapping = None) -> str:
def format_args(args: Sequence[Any] = None, kwargs: Mapping[str, Any] = None) -> str:
"""Format a list of arguments/kwargs to a function-call like string."""
if args is not None:
arglist = [utils.compact_text(repr(arg), 200) for arg in args]
@ -245,9 +249,9 @@ def dbg_signal(sig: pyqtBoundSignal, args: Any) -> str:
return '{}({})'.format(signal_name(sig), format_args(args))
def format_call(func: Callable,
args: Sequence = None,
kwargs: Mapping = None,
def format_call(func: Callable[..., Any],
args: Sequence[Any] = None,
kwargs: Mapping[str, Any] = None,
full: bool = True) -> str:
"""Get a string representation of a function calls with the given args.
@ -302,7 +306,7 @@ class log_time: # noqa: N801,N806 pylint: disable=invalid-name
self._logger.debug("{} took {} seconds.".format(
self._action.capitalize(), delta))
def __call__(self, func: Callable) -> Callable:
def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
@functools.wraps(func)
def wrapped(*args: Any, **kwargs: Any) -> Any:
"""Call the original function."""

View File

@ -25,7 +25,7 @@ import inspect
import os.path
import collections
import enum
from typing import Callable, MutableMapping, Optional, List, Union
from typing import Any, Callable, MutableMapping, Optional, List, Union
import qutebrowser
from qutebrowser.utils import log, utils
@ -88,7 +88,7 @@ class DocstringParser:
arg_inside = enum.auto()
misc = enum.auto()
def __init__(self, func: Callable) -> None:
def __init__(self, func: Callable[..., Any]) -> None:
"""Constructor.
Args:

View File

@ -55,7 +55,8 @@ class CommandOnlyError(Exception):
_IndexType = Union[str, int]
class ObjectRegistry(collections.UserDict):
# UserDict is only generic in Python 3.9+
class ObjectRegistry(collections.UserDict): # type: ignore[type-arg]
"""A registry of long-living objects in qutebrowser.

View File

@ -31,7 +31,8 @@ Module attributes:
import io
import operator
import contextlib
from typing import TYPE_CHECKING, BinaryIO, IO, Iterator, Optional, Union, Tuple, cast
from typing import (Any, AnyStr, TYPE_CHECKING, BinaryIO, IO, Iterator,
Optional, Union, Tuple, cast)
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QFileDevice, QSaveFile, QT_VERSION_STR,
@ -227,7 +228,7 @@ def savefile_open(
filename: str,
binary: bool = False,
encoding: str = 'utf-8'
) -> Iterator[IO]:
) -> Iterator[IO[AnyStr]]:
"""Context manager to easily use a QSaveFile."""
f = QSaveFile(filename)
cancelled = False
@ -239,7 +240,7 @@ def savefile_open(
dev = cast(BinaryIO, PyQIODevice(f))
if binary:
new_f: IO = dev
new_f: IO[Any] = dev # FIXME:mypy Why doesn't AnyStr work?
else:
new_f = io.TextIOWrapper(dev, encoding=encoding)
@ -298,7 +299,11 @@ class PyQIODevice(io.BufferedIOBase):
if not self.writable():
raise OSError("Trying to write to unwritable file!")
def open(self, mode: QIODevice.OpenMode) -> contextlib.closing:
# contextlib.closing is only generic in Python 3.9+
def open(
self,
mode: QIODevice.OpenMode,
) -> contextlib.closing: # type: ignore[type-arg]
"""Open the underlying device and ensure opening succeeded.
Raises OSError if opening failed.

View File

@ -104,7 +104,14 @@ class UrlPattern:
self._init_path(parsed)
self._init_port(parsed)
def _to_tuple(self) -> Tuple:
def _to_tuple(self) -> Tuple[
bool, # _match_all
bool, # _match_subdomains
Optional[str], # _scheme
Optional[str], # host
Optional[str], # _path
Optional[int], # _port
]:
"""Get a pattern with information used for __eq__/__hash__."""
return (self._match_all, self._match_subdomains, self._scheme,
self.host, self._path, self._port)

View File

@ -341,7 +341,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
self._retval = retval
self._predicate = predicate
def __call__(self, func: Callable) -> Callable:
def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
"""Called when a function should be decorated.
Args:
@ -447,7 +447,7 @@ def qualname(obj: Any) -> str:
_ExceptionType = Union[Type[BaseException], Tuple[Type[BaseException]]]
def raises(exc: _ExceptionType, func: Callable, *args: Any) -> bool:
def raises(exc: _ExceptionType, func: Callable[..., Any], *args: Any) -> bool:
"""Check if a function raises a given exception.
Args:
@ -725,7 +725,10 @@ def yaml_dump(data: Any, f: IO[str] = None) -> Optional[str]:
return yaml_data.decode('utf-8')
def chunk(elems: Sequence, n: int) -> Iterator[Sequence]:
_T = TypeVar('_T')
def chunk(elems: Sequence[_T], n: int) -> Iterator[Sequence[_T]]:
"""Yield successive n-sized chunks from elems.
If elems % n != 0, the last chunk will be smaller.