Merge branch 'upstream-stubs'

This commit is contained in:
Florian Bruhin 2020-05-10 20:50:46 +02:00
commit 20ab854510
92 changed files with 611 additions and 452 deletions

View File

@ -19,8 +19,8 @@ matrix:
services: docker
### Archlinux QtWebEngine with testing/KDE-Unstable
# - env: DOCKER=archlinux-webengine-unstable QUTE_BDD_WEBENGINE=true
# services: docker
- env: DOCKER=archlinux-webengine-unstable QUTE_BDD_WEBENGINE=true
services: docker
### PyQt 5.7.1 (Python 3.5)
- python: 3.5

View File

@ -2,8 +2,6 @@
mypy==0.770
mypy-extensions==0.4.3
# PyQt5==5.11.3
# PyQt5-sip==4.19.19
-e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5_stubs
-e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5_stubs
typed-ast==1.4.1
typing-extensions==3.7.4.2

View File

@ -1,6 +1,5 @@
mypy
-e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5-stubs
-e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5-stubs
# remove @commit-id for scm installs
#@ replace: @.*# @wip#
#@ ignore: PyQt5, PyQt5-sip
#@ replace: @.*# @master#

View File

@ -4,23 +4,23 @@
python_version = 3.6
# --strict
warn_redundant_casts = True
warn_unused_ignores = True
warn_unused_configs = True
# disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_decorators = True
## https://github.com/python/mypy/issues/5957
# warn_unused_configs = True
# disallow_untyped_calls = True
# disallow_untyped_defs = True
## https://github.com/python/mypy/issues/5954
# disallow_incomplete_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
# no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
# warn_return_any = True
warn_unreachable = True
# no_implicit_reexport = True
strict_equality = True
# Other strictness flags
strict_equality = True
warn_unreachable = True
# Output
show_error_codes = True
@ -65,76 +65,51 @@ disallow_subclassing_any = False
[mypy-qutebrowser.browser.browsertab]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.browser.hints]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.objects]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.debugcachestats]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.utilcmds]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.throttle]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.backendproblem]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.misc.eventfilter]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.commands.cmdutils]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.config.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.api.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.components.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.extensions.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.browser.webelem]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.browser.webkit.webkitelem]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.browser.webengine.webengineelem]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.keyinput.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.utils.*]
disallow_untyped_defs = True
disallow_incomplete_defs = True
[mypy-qutebrowser.mainwindow.statusbar.command]
disallow_untyped_defs = True
disallow_incomplete_defs = True

View File

@ -208,12 +208,12 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name
raise ValueError("{} has no argument {}!".format(funcname,
self._argname))
if not hasattr(func, 'qute_args'):
func.qute_args = {} # type: ignore
elif func.qute_args is None: # type: ignore
func.qute_args = {} # type: ignore[attr-defined]
elif func.qute_args is None: # type: ignore[attr-defined]
raise ValueError("@cmdutils.argument got called above (after) "
"@cmdutils.register for {}!".format(funcname))
arginfo = command.ArgInfo(**self._kwargs)
func.qute_args[self._argname] = arginfo # type: ignore
func.qute_args[self._argname] = arginfo # type: ignore[attr-defined]
return func

View File

@ -159,13 +159,13 @@ def init(*, args: argparse.Namespace) -> None:
eventfilter.init()
log.init.debug("Connecting signals...")
q_app.focusChanged.connect(on_focus_changed) # type: ignore
q_app.focusChanged.connect(on_focus_changed)
_process_args(args)
for scheme in ['http', 'https', 'qute']:
QDesktopServices.setUrlHandler(
scheme, open_desktopservices_url) # type: ignore
scheme, open_desktopservices_url)
log.init.debug("Init done!")
crashsignal.crash_handler.raise_crashdlg()
@ -482,7 +482,7 @@ class Application(QApplication):
log.init.debug("Initializing application...")
self.launch_time = datetime.datetime.now()
self.focusObjectChanged.connect( # type: ignore
self.focusObjectChanged.connect( # type: ignore[attr-defined]
self.on_focus_object_changed)
self.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

View File

@ -21,8 +21,8 @@
import enum
import itertools
import typing
import functools
import typing
import attr
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
@ -71,7 +71,7 @@ def create(win_id: int,
mode_manager = modeman.instance(win_id)
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginetab
tab_class = webenginetab.WebEngineTab
tab_class = webenginetab.WebEngineTab # type: typing.Type[AbstractTab]
else:
from qutebrowser.browser.webkit import webkittab
tab_class = webkittab.WebKitTab
@ -438,16 +438,15 @@ class AbstractCaret(QObject):
follow_selected_done = pyqtSignal()
def __init__(self,
tab: 'AbstractTab',
mode_manager: modeman.ModeManager,
parent: QWidget = None) -> None:
super().__init__(parent)
self._tab = tab
self._widget = typing.cast(QWidget, None)
self.selection_enabled = False
self._mode_manager = mode_manager
mode_manager.entered.connect(self._on_mode_entered)
mode_manager.left.connect(self._on_mode_left)
# self._tab is set by subclasses so mypy knows its concrete type.
def _on_mode_entered(self, mode: usertypes.KeyMode) -> None:
raise NotImplementedError
@ -542,7 +541,7 @@ class AbstractScroller(QObject):
@pyqtSlot()
def _log_scroll_pos_change(self) -> None:
log.webview.vdebug( # type: ignore
log.webview.vdebug( # type: ignore[attr-defined]
"Scroll position changed to {}".format(self.pos_px()))
def _init_widget(self, widget: QWidget) -> None:
@ -689,9 +688,9 @@ class AbstractElements:
[typing.Optional['webelem.AbstractWebElement']], None]
_ErrorCallback = typing.Callable[[Exception], None]
def __init__(self, tab: 'AbstractTab') -> None:
def __init__(self) -> None:
self._widget = typing.cast(QWidget, None)
self._tab = tab
# self._tab is set by subclasses so mypy knows its concrete type.
def find_css(self, selector: str,
callback: _MultiCallback,
@ -876,8 +875,11 @@ class AbstractTab(QWidget):
# for a given hostname anyways.
_insecure_hosts = set() # type: typing.Set[str]
def __init__(self, *, win_id: int, private: bool,
def __init__(self, *, win_id: int,
mode_manager: modeman.ModeManager,
private: bool,
parent: QWidget = None) -> None:
utils.unused(mode_manager) # needed for mypy
self.is_private = private
self.win_id = win_id
self.tab_id = next(tab_id_gen)
@ -954,7 +956,7 @@ class AbstractTab(QWidget):
log.webview.warning("Unable to find event target!")
return
evt.posted = True
evt.posted = True # type: ignore[attr-defined]
QApplication.postEvent(recipient, evt)
def navigation_blocked(self) -> bool:
@ -1137,7 +1139,8 @@ class AbstractTab(QWidget):
def __repr__(self) -> str:
try:
qurl = self.url()
url = qurl.toDisplayString(QUrl.EncodeUnicode) # type: ignore
url = qurl.toDisplayString(
QUrl.EncodeUnicode) # type: ignore[arg-type]
except (AttributeError, RuntimeError) as exc:
url = '<{}>'.format(exc.__class__.__name__)
else:

View File

@ -157,7 +157,7 @@ class CommandDispatcher:
else:
return None
def _tab_focus_stack(self, mode: str, *, show_error=True):
def _tab_focus_stack(self, mode: str, *, show_error: bool = True) -> None:
"""Select the tab which was last focused."""
tab_deque = self._tabbed_browser.tab_deque
cur_tab = self._cntwidget()
@ -647,11 +647,12 @@ class CommandDispatcher:
def _yank_url(self, what):
"""Helper method for yank() to get the URL to copy."""
assert what in ['url', 'pretty-url'], what
flags = QUrl.RemovePassword
if what == 'pretty-url':
flags |= QUrl.DecodeReserved # type: ignore
flags = QUrl.RemovePassword | QUrl.DecodeReserved
else:
flags |= QUrl.FullyEncoded # type: ignore
flags = QUrl.RemovePassword | QUrl.FullyEncoded
url = QUrl(self._current_url())
url_query = QUrlQuery()
url_query_str = urlutils.query_string(url)
@ -662,7 +663,7 @@ class CommandDispatcher:
if key in config.val.url.yank_ignored_parameters:
url_query.removeQueryItem(key)
url.setQuery(url_query)
return url.toString(flags) # type: ignore
return url.toString(flags) # type: ignore[arg-type]
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('what', choices=['selection', 'url', 'pretty-url',

View File

@ -860,7 +860,7 @@ class AbstractDownloadManager(QObject):
self.data_changed.emit(-1)
@pyqtSlot(str, QUrl)
def _on_pdfjs_requested(self, filename: str, original_url: QUrl):
def _on_pdfjs_requested(self, filename: str, original_url: QUrl) -> None:
"""Open PDF.js when a download requests it."""
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused')

View File

@ -105,7 +105,7 @@ class DownloadView(QListView):
def __repr__(self):
model = self.model()
if model is None:
count = 'None' # type: ignore
count = 'None' # type: ignore[unreachable]
else:
count = model.rowCount()
return utils.get_repr(self, count=count)
@ -132,7 +132,10 @@ class DownloadView(QListView):
item.open_file()
item.remove()
def _get_menu_actions(self, item) -> _ActionListType:
def _get_menu_actions(
self,
item: downloads.AbstractDownloadItem
) -> _ActionListType:
"""Get the available context menu actions for a given DownloadItem.
Args:

View File

@ -235,7 +235,7 @@ class HintActions:
flags = QUrl.FullyEncoded | QUrl.RemovePassword
if url.scheme() == 'mailto':
flags |= QUrl.RemoveScheme
urlstr = url.toString(flags) # type: ignore
urlstr = url.toString(flags) # type: ignore[arg-type]
new_content = urlstr
@ -256,14 +256,15 @@ class HintActions:
def run_cmd(self, url: QUrl, context: HintContext) -> None:
"""Run the command based on a hint URL."""
urlstr = url.toString(QUrl.FullyEncoded) # type: ignore
urlstr = url.toString(QUrl.FullyEncoded) # type: ignore[arg-type]
args = context.get_args(urlstr)
commandrunner = runners.CommandRunner(self._win_id)
commandrunner.run_safely(' '.join(args))
def preset_cmd_text(self, url: QUrl, context: HintContext) -> None:
"""Preset a commandline text based on a hint URL."""
urlstr = url.toDisplayString(QUrl.FullyEncoded) # type: ignore
flags = QUrl.FullyEncoded
urlstr = url.toDisplayString(flags) # type: ignore[arg-type]
args = context.get_args(urlstr)
text = ' '.join(args)
if text[0] not in modeparsers.STARTCHARS:
@ -308,7 +309,8 @@ class HintActions:
}
url = elem.resolve_url(context.baseurl)
if url is not None:
env['QUTE_URL'] = url.toString(QUrl.FullyEncoded) # type: ignore
flags = QUrl.FullyEncoded
env['QUTE_URL'] = url.toString(flags) # type: ignore[arg-type]
try:
userscripts.run_async(context.tab, cmd, *args, win_id=self._win_id,
@ -328,7 +330,7 @@ class HintActions:
context: The HintContext to use.
"""
urlstr = url.toString(
QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore
QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore[arg-type]
args = context.get_args(urlstr)
commandrunner = runners.CommandRunner(self._win_id)
commandrunner.run_safely('spawn ' + ' '.join(args))
@ -893,7 +895,7 @@ class HintManager(QObject):
if self._context.hint_mode == 'number':
# renumber filtered hints
strings = self._hint_strings(visible)
strings = self._hint_strings([label.elem for label in visible])
self._context.labels = {}
for label, string in zip(visible, strings):
label.update_text('', string)

View File

@ -57,7 +57,7 @@ class HistoryProgress:
self._progress.setMinimumDuration(500)
self._progress.setLabelText(text)
self._progress.setMaximum(maximum)
self._progress.setCancelButton(None) # type: ignore
self._progress.setCancelButton(None)
self._progress.show()
QApplication.processEvents()

View File

@ -83,7 +83,7 @@ class AbstractWebInspector(QWidget):
def closeEvent(self, e):
"""Save the geometry when closed."""
data = bytes(self.saveGeometry())
data = self.saveGeometry().data()
geom = base64.b64encode(data).decode('ASCII')
configfiles.state['geometry']['inspector'] = geom

View File

@ -162,8 +162,8 @@ def _find_prevnext(prev, elems):
# pylint: disable=bad-config-option
for regex in getattr(config.val.hints, option):
# pylint: enable=bad-config-option
log.hints.vdebug("== Checking regex '{}'." # type: ignore
.format(regex.pattern))
log.hints.vdebug( # type: ignore[attr-defined]
"== Checking regex '{}'.".format(regex.pattern))
for e in elems:
text = str(e)
if not text:
@ -173,8 +173,8 @@ def _find_prevnext(prev, elems):
regex.pattern, text))
return e
else:
log.hints.vdebug("No match on '{}'!" # type: ignore
.format(text))
log.hints.vdebug( # type: ignore[attr-defined]
"No match on '{}'!".format(text))
return None

View File

@ -65,7 +65,9 @@ def _js_slot(*args):
# pylint: disable=protected-access
return self._error_con.callAsConstructor([e])
# pylint: enable=protected-access
return pyqtSlot(*args, result=QJSValue)(new_method)
deco = pyqtSlot(*args, result=QJSValue) # type: ignore[arg-type]
return deco(new_method)
return _decorator
@ -215,10 +217,10 @@ class PACResolver:
if from_file:
string_flags = QUrl.PrettyDecoded
else:
string_flags = QUrl.RemoveUserInfo # type: ignore
string_flags = QUrl.RemoveUserInfo # type: ignore[assignment]
if query.url().scheme() == 'https':
string_flags |= QUrl.RemovePath # type: ignore
string_flags |= QUrl.RemoveQuery # type: ignore
string_flags |= QUrl.RemovePath # type: ignore[assignment]
string_flags |= QUrl.RemoveQuery # type: ignore[assignment]
result = self._resolver.call([query.url().toString(string_flags),
query.peerHostName()])
@ -266,7 +268,8 @@ class PACFetcher(QObject):
"""Fetch the proxy from the remote URL."""
assert self._manager is not None
self._reply = self._manager.get(QNetworkRequest(self._pac_url))
self._reply.finished.connect(self._finish) # type: ignore
self._reply.finished.connect( # type: ignore[attr-defined]
self._finish)
@pyqtSlot()
def _finish(self):

View File

@ -52,7 +52,8 @@ def _warn_for_pac():
@pyqtSlot()
def shutdown():
QNetworkProxyFactory.setApplicationProxyFactory(None) # type: ignore
QNetworkProxyFactory.setApplicationProxyFactory(
None) # type: ignore[arg-type]
class ProxyFactory(QNetworkProxyFactory):
@ -71,6 +72,18 @@ class ProxyFactory(QNetworkProxyFactory):
else:
return None
def _set_capabilities(self, proxy):
if proxy.type() == QNetworkProxy.NoProxy:
return
capabilities = proxy.capabilities()
lookup_cap = QNetworkProxy.HostNameLookupCapability
if config.val.content.proxy_dns_requests:
capabilities |= lookup_cap
else:
capabilities &= ~lookup_cap
proxy.setCapabilities(capabilities)
def queryProxy(self, query):
"""Get the QNetworkProxies for a query.
@ -89,18 +102,13 @@ class ProxyFactory(QNetworkProxyFactory):
elif isinstance(proxy, pac.PACFetcher):
if objects.backend == usertypes.Backend.QtWebEngine:
# Looks like query.url() is always invalid on QtWebEngine...
proxies = [urlutils.proxy_from_url(QUrl('direct://'))]
proxy = urlutils.proxy_from_url(QUrl('direct://'))
assert not isinstance(proxy, pac.PACFetcher)
proxies = [proxy]
else:
proxies = proxy.resolve(query)
else:
proxies = [proxy]
for p in proxies:
if p.type() != QNetworkProxy.NoProxy:
capabilities = p.capabilities()
lookup_cap = QNetworkProxy.HostNameLookupCapability
if config.val.content.proxy_dns_requests:
capabilities |= lookup_cap # type: ignore
else:
capabilities &= ~lookup_cap # type: ignore
p.setCapabilities(capabilities)
for proxy in proxies:
self._set_capabilities(proxy)
return proxies

View File

@ -84,6 +84,9 @@ def _generate_pdfjs_script(filename):
url_query.addQueryItem('filename', filename)
url.setQuery(url_query)
js_url = javascript.to_js(
url.toString(QUrl.FullyEncoded)) # type: ignore[arg-type]
return jinja.js_environment.from_string("""
document.addEventListener("DOMContentLoaded", function() {
if (typeof window.PDFJS !== 'undefined') {
@ -105,7 +108,7 @@ def _generate_pdfjs_script(filename):
viewer.open({{ url }});
});
""").render(
url=javascript.to_js(url.toString(QUrl.FullyEncoded)), # type: ignore
url=js_url,
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-70420
disable_create_object_url=(
not qtutils.version_check('5.12') and
@ -243,7 +246,7 @@ def get_main_url(filename: str, original_url: QUrl) -> QUrl:
query = QUrlQuery()
query.addQueryItem('filename', filename) # read from our JS
query.addQueryItem('file', '') # to avoid pdfjs opening the default PDF
urlstr = original_url.toString(QUrl.FullyEncoded) # type: ignore
urlstr = original_url.toString(QUrl.FullyEncoded) # type: ignore[arg-type]
query.addQueryItem('source', urlstr)
url.setQuery(query)
return url

View File

@ -39,7 +39,7 @@ try:
import secrets
except ImportError:
# New in Python 3.6
secrets = None # type: ignore
secrets = None # type: ignore[assignment]
from PyQt5.QtCore import QUrlQuery, QUrl, qVersion
@ -98,8 +98,8 @@ class Redirect(Exception):
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
_Handler = TypeVar('_Handler',
bound=Callable[[QUrl], Tuple[str, Union[str, bytes]]])
_HandlerRet = Tuple[str, Union[str, bytes]]
_Handler = TypeVar('_Handler', bound=Callable[[QUrl], _HandlerRet])
class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
@ -125,7 +125,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
return self._function(*args, **kwargs)
def data_for_url(url):
def data_for_url(url: QUrl) -> typing.Tuple[str, bytes]:
"""Get the data to show for the given URL.
Args:
@ -134,8 +134,9 @@ def data_for_url(url):
Return:
A (mimetype, data) tuple.
"""
norm_url = url.adjusted(QUrl.NormalizePathSegments |
QUrl.StripTrailingSlash)
norm_url = url.adjusted(
QUrl.NormalizePathSegments | # type: ignore[arg-type]
QUrl.StripTrailingSlash)
if norm_url != url:
raise Redirect(norm_url)
@ -181,7 +182,7 @@ def data_for_url(url):
@add_handler('bookmarks')
def qute_bookmarks(_url):
def qute_bookmarks(_url: QUrl) -> _HandlerRet:
"""Handler for qute://bookmarks. Display all quickmarks / bookmarks."""
bookmarks = sorted(objreg.get('bookmark-manager').marks.items(),
key=lambda x: x[1]) # Sort by title
@ -196,7 +197,7 @@ def qute_bookmarks(_url):
@add_handler('tabs')
def qute_tabs(_url):
def qute_tabs(_url: QUrl) -> _HandlerRet:
"""Handler for qute://tabs. Display information about all open tabs."""
tabs = collections.defaultdict(
list) # type: typing.Dict[str, typing.List[typing.Tuple[str, str]]]
@ -217,7 +218,10 @@ def qute_tabs(_url):
return 'text/html', src
def history_data(start_time, offset=None):
def history_data(
start_time: float,
offset: int = None
) -> typing.Sequence[typing.Dict[str, typing.Union[str, int]]]:
"""Return history data.
Arguments:
@ -240,7 +244,7 @@ def history_data(start_time, offset=None):
@add_handler('history')
def qute_history(url):
def qute_history(url: QUrl) -> _HandlerRet:
"""Handler for qute://history. Display and serve history."""
if url.path() == '/data':
q_offset = QUrlQuery(url).queryItemValue("offset")
@ -266,7 +270,7 @@ def qute_history(url):
@add_handler('javascript')
def qute_javascript(url):
def qute_javascript(url: QUrl) -> _HandlerRet:
"""Handler for qute://javascript.
Return content of file given as query parameter.
@ -280,14 +284,14 @@ def qute_javascript(url):
@add_handler('pyeval')
def qute_pyeval(_url):
def qute_pyeval(_url: QUrl) -> _HandlerRet:
"""Handler for qute://pyeval."""
src = jinja.render('pre.html', title='pyeval', content=pyeval_output)
return 'text/html', src
@add_handler('spawn-output')
def qute_spawn_output(_url):
def qute_spawn_output(_url: QUrl) -> _HandlerRet:
"""Handler for qute://spawn-output."""
src = jinja.render('pre.html', title='spawn output', content=spawn_output)
return 'text/html', src
@ -304,7 +308,7 @@ def qute_version(_url):
@add_handler('plainlog')
def qute_plainlog(url):
def qute_plainlog(url: QUrl) -> _HandlerRet:
"""Handler for qute://plainlog.
An optional query parameter specifies the minimum log level to print.
@ -323,7 +327,7 @@ def qute_plainlog(url):
@add_handler('log')
def qute_log(url):
def qute_log(url: QUrl) -> _HandlerRet:
"""Handler for qute://log.
An optional query parameter specifies the minimum log level to print.
@ -343,12 +347,12 @@ def qute_log(url):
@add_handler('gpl')
def qute_gpl(_url):
def qute_gpl(_url: QUrl) -> _HandlerRet:
"""Handler for qute://gpl. Return HTML content as string."""
return 'text/html', utils.read_file('html/license.html')
def _asciidoc_fallback_path(html_path):
def _asciidoc_fallback_path(html_path: str) -> typing.Optional[str]:
"""Fall back to plaintext asciidoc if the HTML is unavailable."""
path = html_path.replace('.html', '.asciidoc')
try:
@ -358,7 +362,7 @@ def _asciidoc_fallback_path(html_path):
@add_handler('help')
def qute_help(url):
def qute_help(url: QUrl) -> _HandlerRet:
"""Handler for qute://help."""
urlpath = url.path()
if not urlpath or urlpath == '/':
@ -407,7 +411,7 @@ def qute_help(url):
return 'text/html', data
def _qute_settings_set(url):
def _qute_settings_set(url: QUrl) -> _HandlerRet:
"""Handler for qute://settings/set."""
query = QUrlQuery(url)
option = query.queryItemValue('option', QUrl.FullyDecoded)
@ -429,7 +433,7 @@ def _qute_settings_set(url):
@add_handler('settings')
def qute_settings(url):
def qute_settings(url: QUrl) -> _HandlerRet:
"""Handler for qute://settings. View/change qute configuration."""
global csrf_token
@ -457,7 +461,7 @@ def qute_settings(url):
@add_handler('bindings')
def qute_bindings(_url):
def qute_bindings(_url: QUrl) -> _HandlerRet:
"""Handler for qute://bindings. View keybindings."""
bindings = {}
defaults = config.val.bindings.default
@ -475,7 +479,7 @@ def qute_bindings(_url):
@add_handler('back')
def qute_back(url):
def qute_back(url: QUrl) -> _HandlerRet:
"""Handler for qute://back.
Simple page to free ram / lazy load a site, goes back on focusing the tab.
@ -487,7 +491,7 @@ def qute_back(url):
@add_handler('configdiff')
def qute_configdiff(url):
def qute_configdiff(url: QUrl) -> _HandlerRet:
"""Handler for qute://configdiff."""
if url.path() == '/old':
try:
@ -502,7 +506,7 @@ def qute_configdiff(url):
@add_handler('pastebin-version')
def qute_pastebin_version(_url):
def qute_pastebin_version(_url: QUrl) -> _HandlerRet:
"""Handler that pastebins the version string."""
version.pastebin_version()
return 'text/plain', b'Paste called.'
@ -515,7 +519,7 @@ def _pdf_path(filename: str) -> str:
@add_handler('pdfjs')
def qute_pdfjs(url: QUrl):
def qute_pdfjs(url: QUrl) -> _HandlerRet:
"""Handler for qute://pdfjs.
Return the pdf.js viewer or redirect to original URL if the file does not
@ -566,7 +570,7 @@ def qute_pdfjs(url: QUrl):
@add_handler('warning')
def qute_warning(url):
def qute_warning(url: QUrl) -> _HandlerRet:
"""Handler for qute://warning."""
path = url.path()
if path == '/old-qt':

View File

@ -392,7 +392,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
window.show()
# FIXME:typing Why can't mypy determine the type of
# window.tabbed_browser?
window.tabbed_browser.tabopen(url) # type: ignore
window.tabbed_browser.tabopen(url) # type: ignore[has-type]
else:
raise ValueError("Unknown ClickTarget {}".format(click_target))

View File

@ -24,7 +24,7 @@ import os.path
import urllib
import functools
from PyQt5.QtCore import pyqtSlot, Qt, QUrl
from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QObject
from PyQt5.QtWebEngineWidgets import QWebEngineDownloadItem
from qutebrowser.browser import downloads, pdfjs
@ -39,16 +39,19 @@ class DownloadItem(downloads.AbstractDownloadItem):
_qt_item: The wrapped item.
"""
def __init__(self, qt_item: QWebEngineDownloadItem, parent=None):
def __init__(self, qt_item: QWebEngineDownloadItem,
parent: QObject = None) -> None:
super().__init__(parent)
self._qt_item = qt_item
qt_item.downloadProgress.connect( # type: ignore
qt_item.downloadProgress.connect( # type: ignore[attr-defined]
self.stats.on_download_progress)
qt_item.stateChanged.connect(self._on_state_changed) # type: ignore
qt_item.stateChanged.connect( # type: ignore[attr-defined]
self._on_state_changed)
# Ensure wrapped qt_item is deleted manually when the wrapper object
# is deleted. See https://github.com/qutebrowser/qutebrowser/issues/3373
self.destroyed.connect(self._qt_item.deleteLater) # type: ignore
self.destroyed.connect( # type: ignore[attr-defined]
self._qt_item.deleteLater)
def _is_page_download(self):
"""Check if this item is a page (i.e. mhtml) download."""
@ -93,7 +96,8 @@ class DownloadItem(downloads.AbstractDownloadItem):
"{}".format(state_name))
def _do_die(self):
self._qt_item.downloadProgress.disconnect() # type: ignore
progress_signal = self._qt_item.downloadProgress
progress_signal.disconnect() # type: ignore[attr-defined]
if self._qt_item.state() != QWebEngineDownloadItem.DownloadInterrupted:
self._qt_item.cancel()

View File

@ -248,7 +248,7 @@ class WebEngineElement(webelem.AbstractWebElement):
# (it does so with a 0ms QTimer...)
# This is also used in Qt's tests:
# https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90
QApplication.processEvents( # type: ignore
QApplication.processEvents( # type: ignore[call-overload]
QEventLoop.ExcludeSocketNotifiers |
QEventLoop.ExcludeUserInputEvents)

View File

@ -23,10 +23,10 @@ from PyQt5.QtCore import QBuffer, QIODevice, QUrl
from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler,
QWebEngineUrlRequestJob)
try:
from PyQt5.QtWebEngineCore import QWebEngineUrlScheme # type: ignore
from PyQt5.QtWebEngineCore import QWebEngineUrlScheme
except ImportError:
# Added in Qt 5.12
QWebEngineUrlScheme = None
QWebEngineUrlScheme = None # type: ignore[misc, assignment]
from qutebrowser.browser import qutescheme
from qutebrowser.utils import log, qtutils
@ -165,6 +165,7 @@ def init():
if QWebEngineUrlScheme is not None:
assert not QWebEngineUrlScheme.schemeByName(b'qute').name()
scheme = QWebEngineUrlScheme(b'qute')
scheme.setFlags(QWebEngineUrlScheme.LocalScheme |
QWebEngineUrlScheme.LocalAccessAllowed)
scheme.setFlags(
QWebEngineUrlScheme.LocalScheme | # type: ignore[arg-type]
QWebEngineUrlScheme.LocalAccessAllowed)
QWebEngineUrlScheme.registerScheme(scheme)

View File

@ -362,7 +362,8 @@ def _init_profiles():
default_profile = QWebEngineProfile.defaultProfile()
init_user_agent()
default_profile.setter = ProfileSetter(default_profile)
default_profile.setter = ProfileSetter( # type: ignore[attr-defined]
default_profile)
default_profile.setCachePath(
os.path.join(standarddir.cache(), 'webengine'))
default_profile.setPersistentStoragePath(
@ -372,7 +373,8 @@ def _init_profiles():
if not qtutils.is_single_process():
private_profile = QWebEngineProfile()
private_profile.setter = ProfileSetter(private_profile)
private_profile.setter = ProfileSetter( # type: ignore[attr-defined]
private_profile)
assert private_profile.isOffTheRecord()
private_profile.setter.init_profile()

View File

@ -41,6 +41,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
from qutebrowser.misc import miscwidgets, objects
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
message, objreg, jinja, debug)
from qutebrowser.keyinput import modeman
from qutebrowser.qt import sip
@ -124,7 +125,7 @@ class WebEngineAction(browsertab.AbstractAction):
tb = objreg.get('tabbed-browser', scope='window',
window=self._tab.win_id)
urlstr = self._tab.url().toString(
QUrl.RemoveUserInfo) # type: ignore
QUrl.RemoveUserInfo) # type: ignore[arg-type]
# The original URL becomes the path of a view-source: URL
# (without a host), but query/fragment should stay.
url = QUrl('view-source:' + urlstr)
@ -240,11 +241,14 @@ class WebEngineSearch(browsertab.AbstractSearch):
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._flags = QWebEnginePage.FindFlags(0) # type: ignore
self._flags = self._empty_flags()
self._pending_searches = 0
# The API necessary to stop wrapping was added in this version
self._wrap_handler = _WebEngineSearchWrapHandler()
def _empty_flags(self):
return QWebEnginePage.FindFlags(0) # type: ignore[call-overload]
def connect_signals(self):
self._wrap_handler.connect_signal(self._widget.page())
@ -295,7 +299,7 @@ class WebEngineSearch(browsertab.AbstractSearch):
return
self.text = text
self._flags = QWebEnginePage.FindFlags(0) # type: ignore
self._flags = self._empty_flags()
self._wrap_handler.reset_match_data()
self._wrap_handler.flag_wrap = wrap
if self._is_case_sensitive(ignore_case):
@ -314,7 +318,8 @@ class WebEngineSearch(browsertab.AbstractSearch):
def prev_result(self, *, result_cb=None):
# The int() here makes sure we get a copy of the flags.
flags = QWebEnginePage.FindFlags(int(self._flags)) # type: ignore
flags = QWebEnginePage.FindFlags(
int(self._flags)) # type: ignore[call-overload]
if flags & QWebEnginePage.FindBackward:
if self._wrap_handler.prevent_wrapping(going_up=False):
return
@ -336,6 +341,13 @@ class WebEngineCaret(browsertab.AbstractCaret):
"""QtWebEngine implementations related to moving the cursor/selection."""
def __init__(self,
tab: 'WebEngineTab',
mode_manager: modeman.ModeManager,
parent: QWidget = None) -> None:
super().__init__(mode_manager, parent)
self._tab = tab
def _flags(self):
"""Get flags to pass to JS."""
flags = set()
@ -713,6 +725,10 @@ class WebEngineElements(browsertab.AbstractElements):
"""QtWebEngine implemementations related to elements on the page."""
def __init__(self, tab: 'WebEngineTab') -> None:
super().__init__()
self._tab = tab
def _js_cb_multiple(self, callback, error_cb, js_elems):
"""Handle found elements coming from JS and call the real callback.
@ -1244,7 +1260,10 @@ class WebEngineTab(browsertab.AbstractTab):
abort_questions = pyqtSignal()
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id, private=private, parent=parent)
super().__init__(win_id=win_id,
mode_manager=mode_manager,
private=private,
parent=parent)
widget = webview.WebEngineView(tabdata=self.data, win_id=win_id,
private=private)
self.history = WebEngineHistory(tab=self)
@ -1396,7 +1415,7 @@ class WebEngineTab(browsertab.AbstractTab):
title_url = QUrl(url)
title_url.setScheme('')
title_url_str = title_url.toDisplayString(
QUrl.RemoveScheme) # type: ignore
QUrl.RemoveScheme) # type: ignore[arg-type]
if title == title_url_str.strip('/'):
title = ""
@ -1418,12 +1437,15 @@ class WebEngineTab(browsertab.AbstractTab):
title="Proxy authentication required", text=msg,
mode=usertypes.PromptMode.user_pwd,
abort_on=[self.abort_questions], url=urlstr)
if answer is not None:
authenticator.setUser(answer.user)
authenticator.setPassword(answer.password)
else:
try:
sip.assign(authenticator, QAuthenticator()) # type: ignore
sip.assign( # type: ignore[attr-defined]
authenticator,
QAuthenticator())
except AttributeError:
self._show_error_page(url, "Proxy authentication required")
@ -1444,7 +1466,8 @@ class WebEngineTab(browsertab.AbstractTab):
if not netrc_success and answer is None:
log.network.debug("Aborting auth")
try:
sip.assign(authenticator, QAuthenticator()) # type: ignore
sip.assign( # type: ignore[attr-defined]
authenticator, QAuthenticator())
except AttributeError:
# WORKAROUND for
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-December/038400.html
@ -1714,7 +1737,7 @@ class WebEngineTab(browsertab.AbstractTab):
try:
# pylint: disable=unused-import
from PyQt5.QtWebEngineWidgets import ( # type: ignore
from PyQt5.QtWebEngineWidgets import (
QWebEngineClientCertificateSelection)
except ImportError:
pass
@ -1733,8 +1756,10 @@ class WebEngineTab(browsertab.AbstractTab):
page.loadFinished.connect(self._on_load_finished)
self.before_load_started.connect(self._on_before_load_started)
self.shutting_down.connect(self.abort_questions) # type: ignore
self.load_started.connect(self.abort_questions) # type: ignore
self.shutting_down.connect(
self.abort_questions) # type: ignore[arg-type]
self.load_started.connect(
self.abort_questions) # type: ignore[arg-type]
# pylint: disable=protected-access
self.audio._connect_signals()

View File

@ -216,8 +216,7 @@ class WebEnginePage(QWebEnginePage):
self.shutting_down],
escape_msg=escape_msg)
except shared.CallSuper:
return super().javaScriptPrompt( # type: ignore
url, js_msg, default)
return super().javaScriptPrompt(url, js_msg, default)
def javaScriptAlert(self, url, js_msg):
"""Override javaScriptAlert to use qutebrowser prompts."""

View File

@ -95,7 +95,8 @@ class CookieJar(RAMCookieJar):
"""Parse cookies from lineparser and store them."""
cookies = [] # type: typing.Sequence[QNetworkCookie]
for line in self._lineparser:
cookies += QNetworkCookie.parseCookies(line)
line_cookies = QNetworkCookie.parseCookies(line)
cookies += line_cookies # type: ignore[operator]
self.setAllCookies(cookies)
def purge_old_cookies(self):
@ -104,7 +105,8 @@ class CookieJar(RAMCookieJar):
# http://doc.qt.io/qt-5/qtwebkitexamples-webkitwidgets-browser-cookiejar-cpp.html
now = QDateTime.currentDateTime()
cookies = [c for c in self.allCookies()
if c.isSessionCookie() or c.expirationDate() >= now]
if c.isSessionCookie() or
c.expirationDate() >= now] # type: ignore[operator]
self.setAllCookies(cookies)
def save(self):

View File

@ -169,14 +169,15 @@ class NetworkManager(QNetworkAccessManager):
}
self._set_cookiejar()
self._set_cache()
self.sslErrors.connect(self.on_ssl_errors) # type: ignore
self.sslErrors.connect( # type: ignore[attr-defined]
self.on_ssl_errors)
self._rejected_ssl_errors = collections.defaultdict(
list) # type: _SavedErrorsType
self._accepted_ssl_errors = collections.defaultdict(
list) # type: _SavedErrorsType
self.authenticationRequired.connect( # type: ignore
self.authenticationRequired.connect( # type: ignore[attr-defined]
self.on_authentication_required)
self.proxyAuthenticationRequired.connect( # type: ignore
self.proxyAuthenticationRequired.connect( # type: ignore[attr-defined]
self.on_proxy_authentication_required)
self.netrc_used = False

View File

@ -59,12 +59,15 @@ class FixedDataNetworkReply(QNetworkReply):
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
# pylint: disable=unnecessary-lambda
QTimer.singleShot(0, lambda:
self.metaDataChanged.emit()) # type: ignore
QTimer.singleShot(0, lambda:
self.readyRead.emit()) # type: ignore
QTimer.singleShot(0, lambda:
self.finished.emit()) # type: ignore
QTimer.singleShot(
0,
lambda: self.metaDataChanged.emit()) # type: ignore[attr-defined]
QTimer.singleShot(
0,
lambda: self.readyRead.emit()) # type: ignore[attr-defined]
QTimer.singleShot(
0,
lambda: self.finished.emit()) # type: ignore[attr-defined]
@pyqtSlot()
def abort(self):
@ -120,9 +123,9 @@ class ErrorNetworkReply(QNetworkReply):
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
QTimer.singleShot(0, lambda:
self.error.emit(error)) # type: ignore
self.error.emit(error)) # type: ignore[attr-defined]
QTimer.singleShot(0, lambda:
self.finished.emit()) # type: ignore
self.finished.emit()) # type: ignore[attr-defined]
def abort(self):
"""Do nothing since it's a fake reply."""
@ -150,7 +153,7 @@ class RedirectNetworkReply(QNetworkReply):
super().__init__(parent)
self.setAttribute(QNetworkRequest.RedirectionTargetAttribute, new_url)
QTimer.singleShot(0, lambda:
self.finished.emit()) # type: ignore
self.finished.emit()) # type: ignore[attr-defined]
def abort(self):
"""Called when there's e.g. a redirection limit."""

View File

@ -175,6 +175,11 @@ class WebKitElement(webelem.AbstractWebElement):
self._elem.parent())
if elem is None or elem.isNull():
return None
if typing.TYPE_CHECKING:
# pylint: disable=used-before-assignment
assert isinstance(self._tab, webkittab.WebKitTab)
return WebKitElement(elem, tab=self._tab)
def _rect_on_view_js(self) -> typing.Optional[QRect]:
@ -189,7 +194,7 @@ class WebKitElement(webelem.AbstractWebElement):
return None
text = utils.compact_text(self._elem.toOuterXml(), 500)
log.webelem.vdebug( # type: ignore
log.webelem.vdebug( # type: ignore[attr-defined]
"Client rectangles of element '{}': {}".format(text, rects))
for i in range(int(rects.get("length", 0))):

View File

@ -27,12 +27,14 @@ from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QPoint, QTimer, QSizeF, QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWidgets import QWidget
from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab, shared
from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
webkitsettings)
from qutebrowser.utils import qtutils, usertypes, utils, log, debug
from qutebrowser.keyinput import modeman
from qutebrowser.qt import sip
@ -85,7 +87,10 @@ class WebKitSearch(browsertab.AbstractSearch):
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._flags = QWebPage.FindFlags(0) # type: ignore
self._flags = self._empty_flags()
def _empty_flags(self):
return QWebPage.FindFlags(0) # type: ignore[call-overload]
def _call_cb(self, callback, found, text, flags, caller):
"""Call the given callback if it's non-None.
@ -137,7 +142,7 @@ class WebKitSearch(browsertab.AbstractSearch):
self.text = text
self.search_displayed = True
self._flags = QWebPage.FindFlags(0) # type: ignore
self._flags = self._empty_flags()
if self._is_case_sensitive(ignore_case):
self._flags |= QWebPage.FindCaseSensitively
if reverse:
@ -159,7 +164,8 @@ class WebKitSearch(browsertab.AbstractSearch):
def prev_result(self, *, result_cb=None):
self.search_displayed = True
# The int() here makes sure we get a copy of the flags.
flags = QWebPage.FindFlags(int(self._flags)) # type: ignore
flags = QWebPage.FindFlags(
int(self._flags)) # type: ignore[call-overload]
if flags & QWebPage.FindBackward:
flags &= ~QWebPage.FindBackward
else:
@ -172,6 +178,13 @@ class WebKitCaret(browsertab.AbstractCaret):
"""QtWebKit implementations related to moving the cursor/selection."""
def __init__(self,
tab: 'WebKitTab',
mode_manager: modeman.ModeManager,
parent: QWidget = None) -> None:
super().__init__(mode_manager, parent)
self._tab = tab
@pyqtSlot(usertypes.KeyMode)
def _on_mode_entered(self, mode):
if mode != usertypes.KeyMode.caret:
@ -393,11 +406,11 @@ class WebKitCaret(browsertab.AbstractCaret):
if selected_element is not None:
try:
url = selected_element.attrib['href']
href = selected_element.attrib['href']
except KeyError:
raise browsertab.WebTabError('Anchor element without '
'href!')
url = self._tab.url().resolved(QUrl(url))
url = self._tab.url().resolved(QUrl(href))
if tab:
self._tab.new_tab_requested.emit(url)
else:
@ -582,6 +595,10 @@ class WebKitElements(browsertab.AbstractElements):
"""QtWebKit implemementations related to elements on the page."""
def __init__(self, tab: 'WebKitTab') -> None:
super().__init__()
self._tab = tab
def find_css(self, selector, callback, error_cb, *, only_visible=False):
utils.unused(error_cb)
mainframe = self._widget.page().mainFrame()
@ -699,7 +716,10 @@ class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser."""
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id, private=private, parent=parent)
super().__init__(win_id=win_id,
mode_manager=mode_manager,
private=private,
parent=parent)
widget = webview.WebView(win_id=win_id, tab_id=self.tab_id,
private=private, tab=self)
if private:

View File

@ -21,6 +21,7 @@
import html
import functools
import typing
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint
from PyQt5.QtGui import QDesktopServices
@ -77,22 +78,24 @@ class BrowserPage(QWebPage):
self.setNetworkAccessManager(self._networkmanager)
self.setForwardUnsupportedContent(True)
self.reloading.connect(self._networkmanager.clear_rejected_ssl_errors)
self.printRequested.connect( # type: ignore
self.printRequested.connect( # type: ignore[attr-defined]
self.on_print_requested)
self.downloadRequested.connect( # type: ignore
self.downloadRequested.connect( # type: ignore[attr-defined]
self.on_download_requested)
self.unsupportedContent.connect( # type: ignore
self.unsupportedContent.connect( # type: ignore[attr-defined]
self.on_unsupported_content)
self.loadStarted.connect(self.on_load_started) # type: ignore
self.featurePermissionRequested.connect( # type: ignore
self.loadStarted.connect( # type: ignore[attr-defined]
self.on_load_started)
self.featurePermissionRequested.connect( # type: ignore[attr-defined]
self._on_feature_permission_requested)
self.saveFrameStateRequested.connect( # type: ignore
self.saveFrameStateRequested.connect( # type: ignore[attr-defined]
self.on_save_frame_state_requested)
self.restoreFrameStateRequested.connect( # type: ignore
self.restoreFrameStateRequested.connect( # type: ignore[attr-defined]
self.on_restore_frame_state_requested)
self.loadFinished.connect( # type: ignore
self.loadFinished.connect( # type: ignore[attr-defined]
functools.partial(self._inject_userjs, self.mainFrame()))
self.frameCreated.connect(self._connect_userjs_signals) # type: ignore
self.frameCreated.connect( # type: ignore[attr-defined]
self._connect_userjs_signals)
@pyqtSlot('QWebFrame*')
def _connect_userjs_signals(self, frame):
@ -205,8 +208,10 @@ class BrowserPage(QWebPage):
suggested_file = ""
if info.suggestedFileNames:
suggested_file = info.suggestedFileNames[0]
files.fileNames, _ = QFileDialog.getOpenFileNames(
None, None, suggested_file) # type: ignore
None, None, suggested_file) # type: ignore[arg-type]
return True
def shutdown(self):
@ -348,11 +353,11 @@ class BrowserPage(QWebPage):
self.setFeaturePermission, frame, feature,
QWebPage.PermissionDeniedByUser)
url = frame.url().adjusted(QUrl.RemoveUserInfo |
QUrl.RemovePath |
QUrl.RemoveQuery |
QUrl.RemoveFragment)
url = frame.url().adjusted(typing.cast(QUrl.FormattingOptions,
QUrl.RemoveUserInfo |
QUrl.RemovePath |
QUrl.RemoveQuery |
QUrl.RemoveFragment))
question = shared.feature_permission(
url=url,
option=options[feature], msg=messages[feature],

View File

@ -87,7 +87,8 @@ class WebView(QWebView):
stylesheet.set_register(self)
def __repr__(self):
urlstr = self.url().toDisplayString(QUrl.EncodeUnicode) # type: ignore
flags = QUrl.EncodeUnicode
urlstr = self.url().toDisplayString(flags) # type: ignore[arg-type]
url = utils.elide(urlstr, 100)
return utils.get_repr(self, tab_id=self._tab_id, url=url)
@ -97,7 +98,7 @@ class WebView(QWebView):
# Copied from:
# https://code.google.com/p/webscraping/source/browse/webkit.py#325
try:
self.setPage(None) # type: ignore
self.setPage(None) # type: ignore[arg-type]
except RuntimeError:
# It seems sometimes Qt has already deleted the QWebView and we
# get: RuntimeError: wrapped C/C++ object of type WebView has been
@ -180,9 +181,9 @@ class WebView(QWebView):
This is not needed for QtWebEngine, so it's in here.
"""
menu = self.page().createStandardContextMenu()
self.shutting_down.connect(menu.close) # type: ignore
self.shutting_down.connect(menu.close) # type: ignore[arg-type]
mm = modeman.instance(self.win_id)
mm.entered.connect(menu.close) # type: ignore
mm.entered.connect(menu.close) # type: ignore[arg-type]
menu.exec_(e.globalPos())
def showEvent(self, e):

View File

@ -244,7 +244,7 @@ class Command:
args = self._param_to_argparse_args(param, is_bool)
callsig = debug_utils.format_call(self.parser.add_argument, args,
kwargs, full=False)
log.commands.vdebug( # type: ignore
log.commands.vdebug( # type: ignore[attr-defined]
'Adding arg {} of type {} -> {}'
.format(param.name, typ, callsig))
self.parser.add_argument(*args, **kwargs)
@ -409,7 +409,8 @@ class Command:
if hasattr(typing, 'UnionMeta'):
# Python 3.5.2
# pylint: disable=no-member,useless-suppression
is_union = isinstance(typ, typing.UnionMeta) # type: ignore
is_union = isinstance(
typ, typing.UnionMeta) # type: ignore[attr-defined]
else:
is_union = getattr(typ, '__origin__', None) is typing.Union
@ -575,7 +576,7 @@ class Command:
def register(self):
"""Register this command in objects.commands."""
log.commands.vdebug( # type: ignore
log.commands.vdebug( # type: ignore[attr-defined]
"Registering command {} (from {}:{})".format(
self.name, self.handler.__module__, self.handler.__qualname__))
if self.name in objects.commands:

View File

@ -94,7 +94,7 @@ def _init_variable_replacements() -> typing.Mapping[str, _ReplacementFunction]:
modified_key = '{' + key + '}'
# x = modified_key is to avoid binding x as a closure
replacements[modified_key] = (
lambda _, x=modified_key: x) # type: ignore
lambda _, x=modified_key: x) # type: ignore[misc]
return replacements
@ -332,7 +332,7 @@ class CommandRunner(AbstractCommandRunner):
self._win_id = win_id
@contextlib.contextmanager
def _handle_error(self, safely) -> typing.Iterator[None]:
def _handle_error(self, safely: bool) -> typing.Iterator[None]:
"""Show exceptions as errors if safely=True is given."""
try:
yield

View File

@ -31,6 +31,7 @@ from qutebrowser.commands import runners
from qutebrowser.config import websettings
from qutebrowser.misc import guiprocess
from qutebrowser.browser import downloads
from qutebrowser.qt import sip
class _QtFIFOReader(QObject):
@ -59,8 +60,10 @@ class _QtFIFOReader(QObject):
fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK)
# pylint: enable=no-member,useless-suppression
self._fifo = os.fdopen(fd, 'r')
self._notifier = QSocketNotifier(fd, QSocketNotifier.Read, self)
self._notifier.activated.connect(self.read_line) # type: ignore
self._notifier = QSocketNotifier(typing.cast(sip.voidptr, fd),
QSocketNotifier.Read, self)
self._notifier.activated.connect( # type: ignore[attr-defined]
self.read_line)
@pyqtSlot()
def read_line(self):
@ -263,7 +266,7 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner):
return
self._reader = _QtFIFOReader(self._filepath)
self._reader.got_line.connect(self.got_cmd) # type: ignore
self._reader.got_line.connect(self.got_cmd) # type: ignore[arg-type]
@pyqtSlot()
def on_proc_finished(self):
@ -418,14 +421,13 @@ def run_async(tab, cmd, *args, win_id, env, verbose=False,
verbose: Show notifications when the command started/exited.
output_messages: Show the output as messages.
"""
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser)
tb = objreg.get('tabbed-browser', scope='window', window=win_id)
commandrunner = runners.CommandRunner(win_id, parent=tb)
if utils.is_posix:
runner = _POSIXUserscriptRunner(tabbed_browser)
runner = _POSIXUserscriptRunner(tb) # type: _BaseUserscriptRunner
elif utils.is_windows: # pragma: no cover
runner = _WindowsUserscriptRunner(tabbed_browser)
runner = _WindowsUserscriptRunner(tb)
else: # pragma: no cover
raise UnsupportedError

View File

@ -290,7 +290,7 @@ class CompletionItemDelegate(QStyledItemDelegate):
size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt,
docsize, self._opt.widget)
qtutils.ensure_valid(size)
return size + QSize(10, 3)
return size + QSize(10, 3) # type: ignore[operator]
def paint(self, painter, option, index):
"""Override the QStyledItemDelegate paint function.

View File

@ -278,7 +278,7 @@ class CompletionView(QTreeView):
selmodel.setCurrentIndex(
idx,
QItemSelectionModel.ClearAndSelect | # type: ignore
QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
QItemSelectionModel.Rows)
# if the last item is focused, try to fetch more

View File

@ -183,11 +183,13 @@ class CompletionModel(QAbstractItemModel):
# WORKAROUND:
# layoutChanged is broken in PyQt 5.7.1, so we must use metaObject
# https://www.riverbankcomputing.com/pipermail/pyqt/2017-January/038483.html
self.metaObject().invokeMethod(self, # type: ignore
"layoutAboutToBeChanged")
meta = self.metaObject()
meta.invokeMethod(self, # type: ignore[misc, call-overload]
"layoutAboutToBeChanged")
for cat in self._categories:
cat.set_pattern(pattern)
self.metaObject().invokeMethod(self, "layoutChanged") # type: ignore
meta.invokeMethod(self, # type: ignore[misc, call-overload]
"layoutChanged")
def first_item(self):
"""Return the index of the first child (non-category) in the model."""

View File

@ -21,6 +21,9 @@
import typing
if typing.TYPE_CHECKING:
from PyQt5.QtCore import QAbstractItemModel
from qutebrowser.completion.models import (completionmodel, listcategory,
histcategory)
from qutebrowser.browser import history
@ -74,7 +77,7 @@ def url(*, info):
if k != 'DEFAULT']
# pylint: enable=bad-config-option
categories = config.val.completion.open_categories
models = {}
models = {} # type: typing.Dict[str, QAbstractItemModel]
if searchengines and 'searchengines' in categories:
models['searchengines'] = listcategory.ListCategory(

View File

@ -71,8 +71,10 @@ def _print_preview(tab: apitypes.Tab) -> None:
tab.printing.check_preview_support()
diag = QPrintPreviewDialog(tab)
diag.setAttribute(Qt.WA_DeleteOnClose)
diag.setWindowFlags(diag.windowFlags() | Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint)
diag.setWindowFlags(
diag.windowFlags() | # type: ignore[operator, arg-type]
Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint)
diag.paintRequested.connect(functools.partial(
tab.printing.to_printer, callback=print_callback))
diag.exec_()

View File

@ -206,7 +206,7 @@ class KeyConfig:
'mode'.format(key, mode))
self._validate(key, mode)
log.keyboard.vdebug( # type: ignore
log.keyboard.vdebug( # type: ignore[attr-defined]
"Adding binding {} -> {} in mode {}.".format(key, command, mode))
bindings = self._config.get_mutable_obj('bindings.commands')

View File

@ -161,7 +161,7 @@ class YamlConfig(QObject):
# Instead, create a config.py - see :help for details.
""".lstrip('\n')))
utils.yaml_dump(data, f) # type: ignore
utils.yaml_dump(data, f)
def _pop_object(self,
yaml_data: typing.Any,
@ -400,7 +400,7 @@ class YamlMigrations(QObject):
except KeyError:
continue
if not isinstance(opt.typ, configtypes.Font):
if not isinstance(opt.typ, configtypes.FontBase):
continue
for scope, val in self._settings[name].items():
@ -692,8 +692,8 @@ def read_config_py(filename: str, raising: bool = False) -> None:
basename = os.path.basename(filename)
module = types.ModuleType('config')
module.config = api # type: ignore
module.c = container # type: ignore
module.config = api # type: ignore[attr-defined]
module.c = container # type: ignore[attr-defined]
module.__file__ = filename
try:

View File

@ -121,11 +121,11 @@ def _update_font_defaults(setting: str) -> None:
if setting not in {'fonts.default_family', 'fonts.default_size'}:
return
configtypes.Font.set_defaults(config.val.fonts.default_family,
config.val.fonts.default_size)
configtypes.FontBase.set_defaults(config.val.fonts.default_family,
config.val.fonts.default_size)
for name, opt in configdata.DATA.items():
if not isinstance(opt.typ, configtypes.Font):
if not isinstance(opt.typ, configtypes.FontBase):
continue
value = config.instance.get_obj(name)
@ -165,8 +165,8 @@ def late_init(save_manager: savemanager.SaveManager) -> None:
_init_errors = None
configtypes.Font.set_defaults(config.val.fonts.default_family,
config.val.fonts.default_size)
configtypes.FontBase.set_defaults(config.val.fonts.default_family,
config.val.fonts.default_size)
config.instance.changed.connect(_update_font_defaults)
config.instance.init_save_manager(save_manager)

View File

@ -65,6 +65,7 @@ from qutebrowser.config import configexc, configutils
from qutebrowser.utils import (standarddir, utils, qtutils, urlutils, urlmatch,
usertypes)
from qutebrowser.keyinput import keyutils
from qutebrowser.browser.network import pac
class _SystemProxy:
@ -702,8 +703,10 @@ class Bool(BaseType):
super().__init__(none_ok)
self.valid_values = ValidValues('true', 'false', generate_docs=False)
def to_py(self, value: typing.Optional[bool]) -> typing.Optional[bool]:
def to_py(self,
value: typing.Union[bool, str, None]) -> typing.Optional[bool]:
self._basic_py_validation(value, bool)
assert not isinstance(value, str)
return value
def from_str(self, value: str) -> typing.Optional[bool]:
@ -733,15 +736,15 @@ class BoolAsk(Bool):
super().__init__(none_ok)
self.valid_values = ValidValues('true', 'false', 'ask')
def to_py(self, # type: ignore
def to_py(self, # type: ignore[override]
value: typing.Union[bool, str]) -> typing.Union[bool, str, None]:
# basic validation unneeded if it's == 'ask' and done by Bool if we
# call super().to_py
if isinstance(value, str) and value.lower() == 'ask':
return 'ask'
return super().to_py(value) # type: ignore
return super().to_py(value)
def from_str(self, # type: ignore
def from_str(self, # type: ignore[override]
value: str) -> typing.Union[bool, str, None]:
# basic validation unneeded if it's == 'ask' and done by Bool if we
# call super().from_str
@ -1146,14 +1149,9 @@ class QssColor(BaseType):
return value
class Font(BaseType):
class FontBase(BaseType):
"""A font family, with optional style/weight/size.
* Style: `normal`/`italic`/`oblique`
* Weight: `normal`, `bold`, `100`..`900`
* Size: _number_ `px`/`pt`
"""
"""Base class for Font/QtFont/FontFamily."""
# Gets set when the config is initialized.
default_family = None # type: str
@ -1229,6 +1227,19 @@ class Font(BaseType):
cls.default_family = families.to_str(quote=True)
cls.default_size = default_size
def to_py(self, value: typing.Any) -> typing.Any:
raise NotImplementedError
class Font(FontBase):
"""A font family, with optional style/weight/size.
* Style: `normal`/`italic`/`oblique`
* Weight: `normal`, `bold`, `100`..`900`
* Size: _number_ `px`/`pt`
"""
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
if isinstance(value, usertypes.Unset):
@ -1251,7 +1262,7 @@ class Font(BaseType):
return value
class FontFamily(Font):
class FontFamily(FontBase):
"""A Qt font family."""
@ -1275,7 +1286,7 @@ class FontFamily(Font):
return value
class QtFont(Font):
class QtFont(FontBase):
"""A Font which gets converted to a QFont."""
@ -1338,7 +1349,7 @@ class QtFont(Font):
families = self._parse_families(family_str)
if hasattr(font, 'setFamilies'):
# Added in Qt 5.13
font.setFamily(families.family) # type: ignore
font.setFamily(families.family) # type: ignore[arg-type]
font.setFamilies(list(families))
else: # pragma: no cover
font.setFamily(families.to_str(quote=False))
@ -1713,7 +1724,8 @@ class Proxy(BaseType):
def to_py(
self,
value: _StrUnset
) -> typing.Union[usertypes.Unset, None, QNetworkProxy, _SystemProxy]:
) -> typing.Union[usertypes.Unset, None,
QNetworkProxy, _SystemProxy, pac.PACFetcher]:
self._basic_py_validation(value, str)
if isinstance(value, usertypes.Unset):
return value
@ -1783,7 +1795,10 @@ class FuzzyUrl(BaseType):
"""A URL which gets interpreted as search if needed."""
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
def to_py(
self,
value: _StrUnset
) -> typing.Union[None, QUrl, usertypes.Unset]:
self._basic_py_validation(value, str)
if isinstance(value, usertypes.Unset):
return value
@ -1819,7 +1834,7 @@ class Padding(Dict):
fixed_keys=['top', 'bottom', 'left', 'right'],
none_ok=none_ok)
def to_py( # type: ignore
def to_py( # type: ignore[override]
self,
value: typing.Union[usertypes.Unset, typing.Dict, None],
) -> typing.Union[usertypes.Unset, PaddingValues]:

View File

@ -151,7 +151,7 @@ class Values:
return bool(self._vmap)
def _check_pattern_support(
self, arg: typing.Optional[urlmatch.UrlPattern]) -> None:
self, arg: typing.Union[urlmatch.UrlPattern, QUrl, None]) -> None:
"""Make sure patterns are supported if one was given."""
if arg is not None and not self.opt.supports_pattern:
raise configexc.NoPatternError(self.opt.name)

View File

@ -109,7 +109,7 @@ class _StyleSheetObserver(QObject):
def register(self) -> None:
"""Do a first update and listen for more."""
qss = self._get_stylesheet()
log.config.vdebug( # type: ignore
log.config.vdebug( # type: ignore[attr-defined]
"stylesheet for {}: {}".format(self._obj.__class__.__name__, qss))
self._obj.setStyleSheet(qss)
if self._update:

View File

@ -103,6 +103,9 @@ class AbstractSettings:
def __init__(self, settings: typing.Any) -> None:
self._settings = settings
def _assert_not_unset(self, value: typing.Any) -> None:
assert value is not usertypes.UNSET
def set_attribute(self, name: str, value: typing.Any) -> bool:
"""Set the given QWebSettings/QWebEngineSettings attribute.
@ -139,7 +142,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
assert value is not usertypes.UNSET # type: ignore
self._assert_not_unset(value)
family = self._FONT_SIZES[name]
old_value = self._settings.fontSize(family)
self._settings.setFontSize(family, value)
@ -154,7 +157,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
assert value is not usertypes.UNSET # type: ignore
self._assert_not_unset(value)
family = self._FONT_FAMILIES[name]
if value is None:
font = QFont()
@ -172,7 +175,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
assert encoding is not usertypes.UNSET # type: ignore
self._assert_not_unset(encoding)
old_value = self._settings.defaultTextEncoding()
self._settings.setDefaultTextEncoding(encoding)
return old_value != encoding

View File

@ -82,8 +82,8 @@ def add_module_info(module: types.ModuleType) -> ModuleInfo:
"""Add ModuleInfo to a module (if not added yet)."""
# pylint: disable=protected-access
if not hasattr(module, '__qute_module_info'):
module.__qute_module_info = ModuleInfo() # type: ignore
return module.__qute_module_info # type: ignore
module.__qute_module_info = ModuleInfo() # type: ignore[attr-defined]
return module.__qute_module_info # type: ignore[attr-defined]
def load_components(*, skip_hooks: bool = False) -> None:
@ -109,7 +109,7 @@ def _walk_normal() -> typing.Iterator[ExtensionInfo]:
for _finder, name, ispkg in pkgutil.walk_packages(
# Only packages have a __path__ attribute,
# but we're sure this is one.
path=components.__path__, # type: ignore
path=components.__path__, # type: ignore[attr-defined]
prefix=components.__name__ + '.',
onerror=_on_walk_error):
if ispkg:

View File

@ -26,7 +26,6 @@ import typing
import attr
from PyQt5.QtCore import pyqtSignal, QObject, Qt
from PyQt5.QtGui import QKeySequence, QKeyEvent
from PyQt5.QtWidgets import QWidget
from qutebrowser.config import config
from qutebrowser.utils import usertypes, log, utils
@ -174,7 +173,7 @@ class BaseKeyParser(QObject):
passthrough = False
supports_count = True
def __init__(self, win_id: int, parent: QWidget = None) -> None:
def __init__(self, win_id: int, parent: QObject = None) -> None:
super().__init__(parent)
self._win_id = win_id
self._modename = None

View File

@ -19,6 +19,8 @@
"""Global Qt event filter which dispatches key events."""
import typing
from PyQt5.QtCore import pyqtSlot, QObject, QEvent
from PyQt5.QtGui import QKeyEvent, QWindow
from PyQt5.QtWidgets import QApplication
@ -100,7 +102,7 @@ class EventFilter(QObject):
handler = self._handlers[typ]
try:
return handler(event)
return handler(typing.cast(QKeyEvent, event))
except:
# If there is an exception in here and we leave the eventfilter
# activated, we'll get an infinite loop and a stack overflow.

View File

@ -53,6 +53,8 @@ _MODIFIER_MAP = {
_NIL_KEY = Qt.Key(0)
_ModifierType = typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers]
def _build_special_names() -> typing.Mapping[Qt.Key, str]:
"""Build _SPECIAL_NAMES dict from the special_names_str mapping below.
@ -167,9 +169,10 @@ def _assert_plain_key(key: Qt.Key) -> None:
assert not key & Qt.KeyboardModifierMask, hex(key)
def _assert_plain_modifier(key: Qt.KeyboardModifier) -> None:
def _assert_plain_modifier(key: _ModifierType) -> None:
"""Make sure this is a modifier without a key mixed in."""
assert not key & ~Qt.KeyboardModifierMask, hex(key)
mask = Qt.KeyboardModifierMask
assert not key & ~mask, hex(key) # type: ignore[operator]
def _is_printable(key: Qt.Key) -> bool:
@ -177,7 +180,7 @@ def _is_printable(key: Qt.Key) -> bool:
return key <= 0xff and key not in [Qt.Key_Space, _NIL_KEY]
def is_special_hint_mode(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool:
def is_special_hint_mode(key: Qt.Key, modifiers: _ModifierType) -> bool:
"""Check whether this key should clear the keychain in hint mode.
When we press "s<Escape>", we don't want <Escape> to be handled as part of
@ -192,7 +195,7 @@ def is_special_hint_mode(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool:
Qt.KeypadModifier])
def is_special(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool:
def is_special(key: Qt.Key, modifiers: _ModifierType) -> bool:
"""Check whether this key requires special key syntax."""
_assert_plain_key(key)
_assert_plain_modifier(modifiers)
@ -244,7 +247,7 @@ def _remap_unicode(key: Qt.Key, text: str) -> Qt.Key:
def _check_valid_utf8(s: str,
data: typing.Union[Qt.Key, Qt.KeyboardModifier]) -> None:
data: typing.Union[Qt.Key, _ModifierType]) -> None:
"""Make sure the given string is valid UTF-8.
Makes sure there are no chars where Qt did fall back to weird UTF-16
@ -254,7 +257,7 @@ def _check_valid_utf8(s: str,
s.encode('utf-8')
except UnicodeEncodeError as e: # pragma: no cover
raise ValueError("Invalid encoding in 0x{:x} -> {}: {}"
.format(data, s, e))
.format(int(data), s, e))
def _key_to_string(key: Qt.Key) -> str:
@ -276,15 +279,16 @@ def _key_to_string(key: Qt.Key) -> str:
return result
def _modifiers_to_string(modifiers: Qt.KeyboardModifier) -> str:
def _modifiers_to_string(modifiers: _ModifierType) -> str:
"""Convert the given Qt::KeyboardModifiers to a string.
Handles Qt.GroupSwitchModifier because Qt doesn't handle that as a
modifier.
"""
_assert_plain_modifier(modifiers)
if modifiers & Qt.GroupSwitchModifier:
modifiers &= ~Qt.GroupSwitchModifier # type: ignore
altgr = Qt.GroupSwitchModifier
if modifiers & altgr: # type: ignore[operator]
modifiers &= ~altgr # type: ignore[operator, assignment]
result = 'AltGr+'
else:
result = ''
@ -375,7 +379,7 @@ class KeyInfo:
"""
key = attr.ib() # type: Qt.Key
modifiers = attr.ib() # type: Qt.KeyboardModifier
modifiers = attr.ib() # type: _ModifierType
@classmethod
def from_event(cls, e: QKeyEvent) -> 'KeyInfo':
@ -388,7 +392,7 @@ class KeyInfo:
modifiers = e.modifiers()
_assert_plain_key(key)
_assert_plain_modifier(modifiers)
return cls(key, modifiers)
return cls(key, typing.cast(Qt.KeyboardModifier, modifiers))
def __hash__(self) -> int:
"""Convert KeyInfo to int before hashing.
@ -451,7 +455,7 @@ class KeyInfo:
return ''
text = QKeySequence(self.key).toString()
if not self.modifiers & Qt.ShiftModifier:
if not self.modifiers & Qt.ShiftModifier: # type: ignore[operator]
text = text.lower()
return text
@ -508,7 +512,7 @@ class KeySequence:
"""Iterate over KeyInfo objects."""
for key_and_modifiers in self._iter_keys():
key = Qt.Key(int(key_and_modifiers) & ~Qt.KeyboardModifierMask)
modifiers = Qt.KeyboardModifiers( # type: ignore
modifiers = Qt.KeyboardModifiers( # type: ignore[call-overload]
int(key_and_modifiers) & Qt.KeyboardModifierMask)
yield KeyInfo(key=key, modifiers=modifiers)
@ -565,7 +569,9 @@ class KeySequence:
return infos[item]
def _iter_keys(self) -> typing.Iterator[int]:
return itertools.chain.from_iterable(self._sequences)
sequences = typing.cast(typing.Iterable[typing.Iterable[int]],
self._sequences)
return itertools.chain.from_iterable(sequences)
def _validate(self, keystr: str = None) -> None:
for info in self:
@ -646,7 +652,7 @@ class KeySequence:
if (modifiers == Qt.ShiftModifier and
_is_printable(key) and
not ev.text().isupper()):
modifiers = Qt.KeyboardModifiers()
modifiers = Qt.KeyboardModifiers() # type: ignore[assignment]
# On macOS, swap Ctrl and Meta back
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-51293

View File

@ -20,7 +20,7 @@
"""Mode manager singleton which handles the current keyboard mode."""
import functools
import typing
from typing import Mapping, Callable, MutableMapping, Union, Set, cast
import attr
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent
@ -37,8 +37,7 @@ from qutebrowser.browser import hints
INPUT_MODES = [usertypes.KeyMode.insert, usertypes.KeyMode.passthrough]
PROMPT_MODES = [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]
ParserDictType = typing.MutableMapping[
usertypes.KeyMode, basekeyparser.BaseKeyParser]
ParserDictType = MutableMapping[usertypes.KeyMode, basekeyparser.BaseKeyParser]
@attr.s(frozen=True)
@ -169,7 +168,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager':
return modeman
def instance(win_id: typing.Union[int, str]) -> 'ModeManager':
def instance(win_id: Union[int, str]) -> 'ModeManager':
"""Get a modemanager object."""
return objreg.get('mode-manager', scope='window', window=win_id)
@ -223,7 +222,7 @@ class ModeManager(QObject):
self.parsers = {} # type: ParserDictType
self._prev_mode = usertypes.KeyMode.normal
self.mode = usertypes.KeyMode.normal
self._releaseevents_to_pass = set() # type: typing.Set[KeyEvent]
self._releaseevents_to_pass = set() # type: Set[KeyEvent]
def __repr__(self) -> str:
return utils.get_repr(self, mode=self.mode)
@ -246,9 +245,11 @@ class ModeManager(QObject):
"{}".format(curmode, utils.qualname(parser)))
match = parser.handle(event, dry_run=dry_run)
is_non_alnum = (
event.modifiers() not in [Qt.NoModifier, Qt.ShiftModifier] or
not event.text().strip())
has_modifier = event.modifiers() not in [
Qt.NoModifier,
Qt.ShiftModifier,
] # type: ignore[comparison-overlap]
is_non_alnum = has_modifier or not event.text().strip()
forward_unbound_keys = config.cache['input.forward_unbound_keys']
@ -416,9 +417,9 @@ class ModeManager(QObject):
QEvent.KeyRelease: self._handle_keyrelease,
QEvent.ShortcutOverride:
functools.partial(self._handle_keypress, dry_run=True),
} # type: typing.Mapping[QEvent.Type, typing.Callable[[QEvent], bool]]
} # type: Mapping[QEvent.Type, Callable[[QKeyEvent], bool]]
handler = handlers[event.type()]
return handler(event)
return handler(cast(QKeyEvent, event))
@cmdutils.register(instance='mode-manager', scope='window')
def clear_keychain(self) -> None:

View File

@ -102,7 +102,7 @@ def raise_window(window, alert=True):
window.setWindowState(window.windowState() | Qt.WindowActive)
window.raise_()
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-69568
QCoreApplication.processEvents( # type: ignore
QCoreApplication.processEvents( # type: ignore[call-overload]
QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers)
window.activateWindow()
@ -384,7 +384,9 @@ class MainWindow(QWidget):
self._command_dispatcher,
command_only=True,
scope='window', window=self.win_id)
self.tabbed_browser.widget.destroyed.connect( # type: ignore
widget = self.tabbed_browser.widget
widget.destroyed.connect( # type: ignore[attr-defined]
functools.partial(objreg.delete, 'command-dispatcher',
scope='window', window=self.win_id))
@ -409,7 +411,7 @@ class MainWindow(QWidget):
self._vbox.removeWidget(self.tabbed_browser.widget)
self._vbox.removeWidget(self._downloadview)
self._vbox.removeWidget(self.status)
widgets = [self.tabbed_browser.widget]
widgets = [self.tabbed_browser.widget] # type: typing.List[QWidget]
downloads_position = config.val.downloads.position
if downloads_position == 'top':
@ -446,7 +448,7 @@ class MainWindow(QWidget):
def _save_geometry(self):
"""Save the window geometry to the state config."""
data = bytes(self.saveGeometry())
data = self.saveGeometry().data()
geom = base64.b64encode(data).decode('ASCII')
configfiles.state['geometry']['mainwindow'] = geom
@ -491,15 +493,15 @@ class MainWindow(QWidget):
mode_manager.left.connect(self.status.on_mode_left)
mode_manager.left.connect(self.status.cmd.on_mode_left)
mode_manager.left.connect(
message.global_bridge.mode_left) # type: ignore
message.global_bridge.mode_left) # type: ignore[arg-type]
# commands
normal_parser = mode_manager.parsers[usertypes.KeyMode.normal]
normal_parser.keystring_updated.connect(
self.status.keystring.setText)
self.status.cmd.got_cmd[str].connect( # type: ignore
self.status.cmd.got_cmd[str].connect( # type: ignore[index]
self._commandrunner.run_safely)
self.status.cmd.got_cmd[str, int].connect( # type: ignore
self.status.cmd.got_cmd[str, int].connect( # type: ignore[index]
self._commandrunner.run_safely)
self.status.cmd.returnPressed.connect(
self.tabbed_browser.on_cmd_return_pressed)
@ -574,7 +576,7 @@ class MainWindow(QWidget):
refresh_window = self.isVisible()
if hidden:
window_flags |= Qt.CustomizeWindowHint | Qt.NoDropShadowWindowHint
self.setWindowFlags(window_flags)
self.setWindowFlags(typing.cast(Qt.WindowFlags, window_flags))
if refresh_window:
self.show()
@ -583,8 +585,9 @@ class MainWindow(QWidget):
if not config.val.content.fullscreen.window:
if on:
self.state_before_fullscreen = self.windowState()
self.setWindowState(Qt.WindowFullScreen | # type: ignore
self.state_before_fullscreen)
self.setWindowState(
Qt.WindowFullScreen | # type: ignore[arg-type]
self.state_before_fullscreen) # type: ignore[operator]
elif self.isFullScreen():
self.setWindowState(self.state_before_fullscreen)
log.misc.debug('on: {}, state before fullscreen: {}'.format(

View File

@ -192,12 +192,14 @@ class PromptQueue(QObject):
if blocking:
loop = qtutils.EventLoop()
self._loops.append(loop)
loop.destroyed.connect( # type: ignore
loop.destroyed.connect( # type: ignore[attr-defined]
lambda: self._loops.remove(loop))
question.completed.connect(loop.quit)
question.completed.connect(loop.deleteLater)
log.prompt.debug("Starting loop.exec_() for {}".format(question))
loop.exec_(QEventLoop.ExcludeSocketNotifiers)
flags = typing.cast(QEventLoop.ProcessEventsFlags,
QEventLoop.ExcludeSocketNotifiers)
loop.exec_(flags)
log.prompt.debug("Ending loop.exec_() for {}".format(question))
log.prompt.debug("Restoring old question {}".format(old_question))
@ -329,7 +331,7 @@ class PromptContainer(QWidget):
usertypes.PromptMode.alert: AlertPrompt,
}
klass = classes[question.mode]
prompt = typing.cast(_BasePrompt, klass(question))
prompt = klass(question)
log.prompt.debug("Displaying prompt {}".format(prompt))
self._prompt = prompt
@ -707,7 +709,7 @@ class FilenamePrompt(_BasePrompt):
# Nothing selected initially
self._file_view.setCurrentIndex(QModelIndex())
# The model needs to be sorted so we get the correct first/last index
self._file_model.directoryLoaded.connect( # type: ignore
self._file_model.directoryLoaded.connect(
lambda: self._file_model.sort(0))
def accept(self, value=None, save=False):
@ -755,7 +757,7 @@ class FilenamePrompt(_BasePrompt):
selmodel.setCurrentIndex(
idx,
QItemSelectionModel.ClearAndSelect | # type: ignore
QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
QItemSelectionModel.Rows)
self._insert_path(idx, clicked=False)
@ -782,7 +784,7 @@ class DownloadFilenamePrompt(FilenamePrompt):
def __init__(self, question, parent=None):
super().__init__(question, parent)
self._file_model.setFilter(
QDir.AllDirs | QDir.Drives | QDir.NoDot) # type: ignore
QDir.AllDirs | QDir.Drives | QDir.NoDot) # type: ignore[arg-type]
def accept(self, value=None, save=False):
done = super().accept(value, save)
@ -960,5 +962,5 @@ def init():
"""Initialize global prompt objects."""
global prompt_queue
prompt_queue = PromptQueue()
message.global_bridge.ask_question.connect( # type: ignore
message.global_bridge.ask_question.connect( # type: ignore[call-arg]
prompt_queue.ask_question, Qt.DirectConnection)

View File

@ -21,7 +21,8 @@
import enum
import attr
from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, # type: ignore[attr-defined]
pyqtProperty, Qt, QSize, QTimer)
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
from qutebrowser.browser import browsertab

View File

@ -72,8 +72,9 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored)
self.cursorPositionChanged.connect(
self.update_completion) # type: ignore
self.textChanged.connect(self.update_completion) # type: ignore
self.update_completion) # type: ignore[arg-type]
self.textChanged.connect(
self.update_completion) # type: ignore[arg-type]
self.textChanged.connect(self.updateGeometry)
self.textChanged.connect(self._incremental_search)
@ -148,7 +149,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
raise cmdutils.CommandError(
"Invalid command text '{}'.".format(text))
if run_on_count and count is not None:
self.got_cmd[str, int].emit(text, count) # type: ignore
self.got_cmd[str, int].emit(text, count) # type: ignore[index]
else:
self.set_cmd_text(text)
@ -198,7 +199,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
'cmd accept')
if not was_search:
self.got_cmd[str].emit(text[1:]) # type: ignore
self.got_cmd[str].emit(text[1:]) # type: ignore[index]
@cmdutils.register(instance='status-command', scope='window')
def edit_command(self, run: bool = False) -> None:

View File

@ -21,7 +21,8 @@
import enum
from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl
from PyQt5.QtCore import (pyqtSlot, pyqtProperty, # type: ignore[attr-defined]
QUrl)
from qutebrowser.mainwindow.statusbar import textbase
from qutebrowser.config import stylesheet

View File

@ -97,7 +97,7 @@ class TabDeque:
self._ignore_next = True
return tab
def next(self, cur_tab: QWidget, *, keep_overflow=True) -> QWidget:
def next(self, cur_tab: QWidget, *, keep_overflow: bool = True) -> QWidget:
"""Get the 'next' tab in the stack.
Throws IndexError on failure.
@ -204,11 +204,10 @@ class TabbedBrowser(QWidget):
self._tab_insert_idx_left = 0
self._tab_insert_idx_right = -1
self.shutting_down = False
self.widget.tabCloseRequested.connect( # type: ignore
self.on_tab_close_requested)
self.widget.new_tab_requested.connect(self.tabopen)
self.widget.currentChanged.connect( # type: ignore
self._on_current_changed)
self.widget.tabCloseRequested.connect(self.on_tab_close_requested)
self.widget.new_tab_requested.connect(
self.tabopen) # type: ignore[arg-type]
self.widget.currentChanged.connect(self._on_current_changed)
self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide)
self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@ -286,7 +285,7 @@ class TabbedBrowser(QWidget):
for i in range(self.widget.count()):
widget = self.widget.widget(i)
if widget is None:
log.webview.debug( # type: ignore
log.webview.debug( # type: ignore[unreachable]
"Got None-widget in tabbedbrowser!")
else:
widgets.append(widget)
@ -506,10 +505,7 @@ class TabbedBrowser(QWidget):
newtab = self.widget.widget(0)
use_current_tab = False
else:
# FIXME:typing mypy thinks this is None due to @pyqtSlot
newtab = typing.cast(
browsertab.AbstractTab,
self.tabopen(background=False, idx=entry.index))
newtab = self.tabopen(background=False, idx=entry.index)
newtab.history.private_api.deserialize(entry.history)
self.widget.set_tab_pinned(newtab, entry.pinned)
@ -533,7 +529,7 @@ class TabbedBrowser(QWidget):
"""Close a tab via an index."""
tab = self.widget.widget(idx)
if tab is None:
log.webview.debug( # type: ignore
log.webview.debug( # type: ignore[unreachable]
"Got invalid tab {} for index {}!".format(tab, idx))
return
self.tab_close_prompt_if_pinned(
@ -805,7 +801,7 @@ class TabbedBrowser(QWidget):
"""Give focus to current tab if command mode was left."""
widget = self.widget.currentWidget()
if widget is None:
return # type: ignore
return # type: ignore[unreachable]
if mode in [usertypes.KeyMode.command] + modeman.PROMPT_MODES:
log.modes.debug("Left status-input mode, focusing {!r}".format(
widget))
@ -822,7 +818,7 @@ class TabbedBrowser(QWidget):
return
tab = self.widget.widget(idx)
if tab is None:
log.webview.debug( # type: ignore
log.webview.debug( # type: ignore[unreachable]
"on_current_changed got called with invalid index {}"
.format(idx))
return

View File

@ -60,10 +60,11 @@ class TabWidget(QTabWidget):
bar = TabBar(win_id, self)
self.setStyle(TabBarStyle())
self.setTabBar(bar)
bar.tabCloseRequested.connect(self.tabCloseRequested) # type: ignore
bar.tabMoved.connect(functools.partial( # type: ignore
bar.tabCloseRequested.connect(
self.tabCloseRequested) # type: ignore[arg-type]
bar.tabMoved.connect(functools.partial(
QTimer.singleShot, 0, self.update_tab_titles))
bar.currentChanged.connect(self._on_current_changed) # type: ignore
bar.currentChanged.connect(self._on_current_changed)
bar.new_tab_requested.connect(self._on_new_tab_requested)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setDocumentMode(True)
@ -82,7 +83,8 @@ class TabWidget(QTabWidget):
position = config.val.tabs.position
selection_behavior = config.val.tabs.select_on_remove
self.setTabPosition(position)
tabbar.vertical = position in [QTabWidget.West, QTabWidget.East]
tabbar.vertical = position in [ # type: ignore[attr-defined]
QTabWidget.West, QTabWidget.East]
tabbar.setSelectionBehaviorOnRemove(selection_behavior)
tabbar.refresh()
@ -163,7 +165,7 @@ class TabWidget(QTabWidget):
"""Get the tab field data."""
tab = self.widget(idx)
if tab is None:
log.misc.debug( # type: ignore
log.misc.debug( # type: ignore[unreachable]
"Got None-tab in get_tab_fields!")
page_title = self.page_title(idx)
@ -330,7 +332,7 @@ class TabWidget(QTabWidget):
"""
tab = self.widget(idx)
if tab is None:
url = QUrl() # type: ignore
url = QUrl() # type: ignore[unreachable]
else:
url = tab.url()
# It's possible for url to be invalid, but the caller will handle that.
@ -350,7 +352,7 @@ class TabWidget(QTabWidget):
if config.val.tabs.tabs_are_windows:
self.window().setWindowIcon(self.window().windowIcon())
def setTabIcon(self, idx: int, icon: QIcon):
def setTabIcon(self, idx: int, icon: QIcon) -> None:
"""Always show tab icons for pinned tabs in some circumstances."""
tab = typing.cast(typing.Optional[browsertab.AbstractTab],
self.widget(idx))
@ -544,7 +546,7 @@ class TabBar(QTabBar):
idx = self.currentIndex()
elif action == 'close-last':
idx = self.count() - 1
self.tabCloseRequested.emit(idx) # type: ignore
self.tabCloseRequested.emit(idx)
return
super().mousePressEvent(e)

View File

@ -55,7 +55,7 @@ class PyPIVersionClient(QObject):
self._client = httpclient.HTTPClient(self)
else:
self._client = client
self._client.error.connect(self.error) # type: ignore
self._client.error.connect(self.error) # type: ignore[arg-type]
self._client.success.connect(self.on_client_success)
def get_version(self, package='qutebrowser'):

View File

@ -30,12 +30,12 @@ try:
except ImportError: # pragma: no cover
try:
# Python2
from Tkinter import Tk # type: ignore
from Tkinter import Tk # type: ignore[import, no-redef]
import tkMessageBox as messagebox # type: ignore # noqa: N813
except ImportError:
# Some Python without Tk
Tk = None # type: ignore
messagebox = None # type: ignore
Tk = None # type: ignore[misc, assignment]
messagebox = None # type: ignore[assignment]
# First we check the version of Python. This code should run fine with python2
@ -49,7 +49,7 @@ def check_python_version():
version_str = '.'.join(map(str, sys.version_info[:3]))
text = ("At least Python 3.5.2 is required to run qutebrowser, but " +
"it's running with " + version_str + ".\n")
if (Tk and # type: ignore
if (Tk and # type: ignore[unreachable]
'--no-err-windows' not in sys.argv): # pragma: no cover
root = Tk()
root.withdraw()

View File

@ -345,7 +345,7 @@ class _CrashDialog(QDialog):
text: The paste text to show.
"""
error_dlg = ReportErrorDialog(text, self._paste_text, self)
error_dlg.finished.connect(self.finish) # type: ignore
error_dlg.finished.connect(self.finish)
error_dlg.show()
@pyqtSlot(str)
@ -532,7 +532,7 @@ class FatalCrashDialog(_CrashDialog):
if self._chk_history.isChecked():
try:
if history.web_history is None:
history_data = '<unavailable>' # type: ignore
history_data = '<unavailable>' # type: ignore[unreachable]
else:
history_data = '\n'.join(str(e) for e in
history.web_history.get_recent())
@ -629,7 +629,7 @@ class ReportErrorDialog(QDialog):
hbox = QHBoxLayout()
hbox.addStretch()
btn = QPushButton("Close")
btn.clicked.connect(self.close) # type: ignore
btn.clicked.connect(self.close) # type: ignore[arg-type]
hbox.addWidget(btn)
vbox.addLayout(hbox)

View File

@ -44,6 +44,7 @@ from PyQt5.QtWidgets import QApplication
from qutebrowser.api import cmdutils
from qutebrowser.misc import earlyinit, crashdialog, ipc, objects
from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils
from qutebrowser.qt import sip
if typing.TYPE_CHECKING:
from qutebrowser.misc import quitter
@ -184,7 +185,7 @@ class CrashHandler(QObject):
if sys.__stderr__ is not None:
faulthandler.enable(sys.__stderr__)
else:
faulthandler.disable() # type: ignore
faulthandler.disable() # type: ignore[unreachable]
try:
self._crash_log_file.close()
os.remove(self._crash_log_file.name)
@ -351,9 +352,10 @@ class SignalHandler(QObject):
for fd in [read_fd, write_fd]:
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self._notifier = QSocketNotifier(read_fd, QSocketNotifier.Read,
self._notifier = QSocketNotifier(typing.cast(sip.voidptr, read_fd),
QSocketNotifier.Read,
self)
self._notifier.activated.connect( # type: ignore
self._notifier.activated.connect( # type: ignore[attr-defined]
self.handle_signal_wakeup)
self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd)
# pylint: enable=import-error,no-member,useless-suppression

View File

@ -38,7 +38,7 @@ import datetime
try:
import tkinter
except ImportError:
tkinter = None # type: ignore
tkinter = None # type: ignore[assignment]
# NOTE: No qutebrowser or PyQt import should be done here, as some early
# initialization needs to take place before that!
@ -251,7 +251,8 @@ def configure_pyqt():
from PyQt5 import QtCore
QtCore.pyqtRemoveInputHook()
try:
QtCore.pyqt5_enable_new_onexit_scheme(True) # type: ignore
QtCore.pyqt5_enable_new_onexit_scheme( # type: ignore[attr-defined]
True)
except AttributeError:
# Added in PyQt 5.13 somewhere, going to be the default in 5.14
pass
@ -259,7 +260,7 @@ def configure_pyqt():
from qutebrowser.qt import sip
try:
# Added in sip 4.19.4
sip.enableoverflowchecking(True) # type: ignore
sip.enableoverflowchecking(True) # type: ignore[attr-defined]
except AttributeError:
pass

View File

@ -187,7 +187,7 @@ class ExternalEditor(QObject):
if not ok:
log.procs.error("Failed to watch path: {}"
.format(self._filename))
self._watcher.fileChanged.connect( # type: ignore
self._watcher.fileChanged.connect( # type: ignore[attr-defined]
self._on_file_changed)
args = [self._sub_placeholder(arg, line, column) for arg in editor[1:]]

View File

@ -62,12 +62,12 @@ class GUIProcess(QObject):
self.args = None
self._proc = QProcess(self)
self._proc.errorOccurred.connect(self._on_error) # type: ignore
self._proc.errorOccurred.connect(self.error) # type: ignore
self._proc.finished.connect(self._on_finished) # type: ignore
self._proc.finished.connect(self.finished) # type: ignore
self._proc.started.connect(self._on_started) # type: ignore
self._proc.started.connect(self.started) # type: ignore
self._proc.errorOccurred.connect(self._on_error)
self._proc.errorOccurred.connect(self.error) # type: ignore[arg-type]
self._proc.finished.connect(self._on_finished)
self._proc.finished.connect(self.finished) # type: ignore[arg-type]
self._proc.started.connect(self._on_started)
self._proc.started.connect(self.started) # type: ignore[arg-type]
if additional_env is not None:
procenv = QProcessEnvironment.systemEnvironment()
@ -89,9 +89,9 @@ class GUIProcess(QObject):
code, status))
encoding = locale.getpreferredencoding(do_setlocale=False)
stderr = bytes(self._proc.readAllStandardError()).decode(
stderr = self._proc.readAllStandardError().data().decode(
encoding, 'replace')
stdout = bytes(self._proc.readAllStandardOutput()).decode(
stdout = self._proc.readAllStandardOutput().data().decode(
encoding, 'replace')
if self._output_messages:
@ -163,7 +163,8 @@ class GUIProcess(QObject):
"""Convenience wrapper around QProcess::startDetached."""
log.procs.debug("Starting detached.")
self._pre_start(cmd, args)
ok, _pid = self._proc.startDetached(cmd, args, None) # type: ignore
ok, _pid = self._proc.startDetached(
cmd, args, None) # type: ignore[call-arg]
if not ok:
message.error("Error while spawning {}".format(self._what))

View File

@ -178,7 +178,7 @@ class IPCServer(QObject):
self._atime_timer.setTimerType(Qt.VeryCoarseTimer)
self._server = QLocalServer(self)
self._server.newConnection.connect( # type: ignore
self._server.newConnection.connect( # type: ignore[attr-defined]
self.handle_connection)
self._socket = None
@ -252,21 +252,24 @@ class IPCServer(QObject):
return
socket = self._server.nextPendingConnection()
if socket is None:
log.ipc.debug("No new connection to handle.") # type: ignore
log.ipc.debug( # type: ignore[unreachable]
"No new connection to handle.")
return
log.ipc.debug("Client connected (socket 0x{:x}).".format(id(socket)))
self._timer.start()
self._socket = socket
socket.readyRead.connect(self.on_ready_read) # type: ignore
socket.readyRead.connect( # type: ignore[attr-defined]
self.on_ready_read)
if socket.canReadLine():
log.ipc.debug("We can read a line immediately.")
self.on_ready_read()
socket.error.connect(self.on_error) # type: ignore
socket.error.connect(self.on_error) # type: ignore[attr-defined]
if socket.error() not in [QLocalSocket.UnknownSocketError,
QLocalSocket.PeerClosedError]:
log.ipc.debug("We got an error immediately.")
self.on_error(socket.error())
socket.disconnected.connect(self.on_disconnected) # type: ignore
socket.disconnected.connect( # type: ignore[attr-defined]
self.on_disconnected)
if socket.state() == QLocalSocket.UnconnectedState:
log.ipc.debug("Socket was disconnected immediately.")
self.on_disconnected()

View File

@ -37,7 +37,7 @@ class MinimalLineEditMixin:
"""A mixin to give a QLineEdit a minimal look and nicer repr()."""
def __init__(self):
self.setStyleSheet( # type: ignore
self.setStyleSheet( # type: ignore[attr-defined]
"""
QLineEdit {
border: 0px;
@ -46,7 +46,8 @@ class MinimalLineEditMixin:
}
"""
)
self.setAttribute(Qt.WA_MacShowFocusRect, False) # type: ignore
self.setAttribute( # type: ignore[attr-defined]
Qt.WA_MacShowFocusRect, False)
def keyPressEvent(self, e):
"""Override keyPressEvent to paste primary selection on Shift + Ins."""
@ -57,9 +58,9 @@ class MinimalLineEditMixin:
e.ignore()
else:
e.accept()
self.insert(text) # type: ignore
self.insert(text) # type: ignore[attr-defined]
return
super().keyPressEvent(e) # type: ignore
super().keyPressEvent(e) # type: ignore[misc]
def __repr__(self):
return utils.get_repr(self)
@ -260,7 +261,7 @@ class WrapperLayout(QLayout):
widget.setParent(container)
def unwrap(self):
self._widget.setParent(None) # type: ignore
self._widget.setParent(None) # type: ignore[call-overload]
self._widget.deleteLater()

View File

@ -60,7 +60,7 @@ def msgbox(parent, title, text, *, icon, buttons=QMessageBox.Ok,
box.setIcon(icon)
box.setStandardButtons(buttons)
if on_finished is not None:
box.finished.connect(on_finished) # type: ignore
box.finished.connect(on_finished)
if plain_text:
box.setTextFormat(Qt.PlainText)
elif plain_text is not None:

View File

@ -323,10 +323,11 @@ class SessionManager(QObject):
else:
data = self._save_all(only_window=only_window,
with_private=with_private)
log.sessions.vdebug("Saving data: {}".format(data)) # type: ignore
log.sessions.vdebug( # type: ignore[attr-defined]
"Saving data: {}".format(data))
try:
with qtutils.savefile_open(path) as f:
utils.yaml_dump(data, f) # type: ignore
utils.yaml_dump(data, f)
except (OSError, UnicodeEncodeError, yaml.YAMLError) as e:
raise SessionError(e)
@ -586,7 +587,7 @@ def session_save(name: ArgType = default, *,
@cmdutils.register()
@cmdutils.argument('name', completion=miscmodels.session)
def session_delete(name, *, force: bool = False) -> None:
def session_delete(name: str, *, force: bool = False) -> None:
"""Delete a session.
Args:

View File

@ -138,7 +138,8 @@ def split(s, keep=False):
out = []
spaces = ""
log.shlexer.vdebug("{!r} -> {!r}".format(s, tokens)) # type: ignore
log.shlexer.vdebug( # type: ignore[attr-defined]
"{!r} -> {!r}".format(s, tokens))
for t in tokens:
if t.isspace():

View File

@ -190,7 +190,8 @@ class Query:
raise BugError("Cannot iterate inactive query")
rec = self.query.record()
fields = [rec.fieldName(i) for i in range(rec.count())]
rowtype = collections.namedtuple('ResultRow', fields) # type: ignore
rowtype = collections.namedtuple( # type: ignore[misc]
'ResultRow', fields)
while self.query.next():
rec = self.query.record()

View File

@ -25,4 +25,4 @@
try:
from PyQt5 import sip
except ImportError:
import sip # type: ignore
import sip # type: ignore[import, no-redef]

View File

@ -31,6 +31,7 @@ from PyQt5.QtCore import Qt, QEvent, QMetaMethod, QObject, pyqtSignal
from PyQt5.QtWidgets import QApplication
from qutebrowser.utils import log, utils, qtutils, objreg
from qutebrowser.qt import sip
def log_events(klass: typing.Type) -> typing.Type:
@ -69,7 +70,7 @@ def log_signals(obj: QObject) -> QObject:
meta_method = metaobj.method(i)
qtutils.ensure_valid(meta_method)
if meta_method.methodType() == QMetaMethod.Signal:
name = bytes(meta_method.name()).decode('ascii')
name = meta_method.name().data().decode('ascii')
if name != 'destroyed':
signal = getattr(obj, name)
try:
@ -79,7 +80,7 @@ def log_signals(obj: QObject) -> QObject:
pass
if inspect.isclass(obj):
old_init = obj.__init__ # type: ignore
old_init = obj.__init__ # type: ignore[misc]
@functools.wraps(old_init)
def new_init(self: typing.Any,
@ -89,7 +90,7 @@ def log_signals(obj: QObject) -> QObject:
old_init(self, *args, **kwargs)
connect_log_slot(self)
obj.__init__ = new_init # type: ignore
obj.__init__ = new_init # type: ignore[misc]
else:
connect_log_slot(obj)
@ -97,7 +98,7 @@ def log_signals(obj: QObject) -> QObject:
def qenum_key(base: typing.Type,
value: int,
value: typing.Union[int, sip.simplewrapper],
add_base: bool = False,
klass: typing.Type = None) -> str:
"""Convert a Qt Enum value to its key as a string.
@ -121,7 +122,7 @@ def qenum_key(base: typing.Type,
try:
idx = base.staticMetaObject.indexOfEnumerator(klass.__name__)
meta_enum = base.staticMetaObject.enumerator(idx)
ret = meta_enum.valueToKey(int(value))
ret = meta_enum.valueToKey(int(value)) # type: ignore[arg-type]
except AttributeError:
ret = None
@ -131,7 +132,7 @@ def qenum_key(base: typing.Type,
ret = name
break
else:
ret = '0x{:04x}'.format(int(value))
ret = '0x{:04x}'.format(int(value)) # type: ignore[arg-type]
if add_base and hasattr(base, '__name__'):
return '.'.join([base.__name__, ret])
@ -140,7 +141,7 @@ def qenum_key(base: typing.Type,
def qflags_key(base: typing.Type,
value: int,
value: typing.Union[int, sip.simplewrapper],
add_base: bool = False,
klass: typing.Type = None) -> str:
"""Convert a Qt QFlags value to its keys as string.
@ -174,7 +175,7 @@ def qflags_key(base: typing.Type,
bits = []
names = []
mask = 0x01
value = int(value)
value = int(value) # type: ignore[arg-type]
while mask <= value:
if value & mask:
bits.append(mask)
@ -182,7 +183,8 @@ def qflags_key(base: typing.Type,
for bit in bits:
# We have to re-convert to an enum type here or we'll sometimes get an
# empty string back.
names.append(qenum_key(base, klass(bit), add_base))
enum_value = klass(bit) # type: ignore[call-arg]
names.append(qenum_key(base, enum_value, add_base))
return '|'.join(names)
@ -208,14 +210,14 @@ def signal_name(sig: pyqtSignal) -> str:
# sig.signal == '2signal1'
# sig.signal == '2signal2(QString,QString)'
m = re.fullmatch(r'[0-9]+(?P<name>.*)\(.*\)',
sig.signal) # type: ignore
sig.signal) # type: ignore[attr-defined]
elif hasattr(sig, 'signatures'):
# Unbound signal, PyQt >= 5.11
# Examples:
# sig.signatures == ('signal1()',)
# sig.signatures == ('signal2(QString,QString)',)
m = re.fullmatch(r'(?P<name>.*)\(.*\)',
sig.signatures[0]) # type: ignore
sig.signatures[0]) # type: ignore[attr-defined]
else: # pragma: no cover
# Unbound signal, PyQt < 5.11
# Examples:

View File

@ -112,7 +112,7 @@ class Environment(jinja2.Environment):
"""
image = utils.resource_filename(path)
url = QUrl.fromLocalFile(image)
urlstr = url.toString(QUrl.FullyEncoded) # type: ignore
urlstr = url.toString(QUrl.FullyEncoded) # type: ignore[arg-type]
return urlstr
def _data_url(self, path: str) -> str:
@ -156,11 +156,11 @@ def template_config_variables(template: str) -> typing.FrozenSet[str]:
# For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'.
attrlist = [] # type: typing.List[str]
while isinstance(node, jinja2.nodes.Getattr):
attrlist.append(node.attr) # type: ignore
node = node.node # type: ignore
attrlist.append(node.attr) # type: ignore[attr-defined]
node = node.node # type: ignore[attr-defined]
if isinstance(node, jinja2.nodes.Name):
if node.name == 'conf': # type: ignore
if node.name == 'conf': # type: ignore[attr-defined]
result.add('.'.join(reversed(attrlist)))
# otherwise, the node is a Name node so it doesn't have any
# child nodes

View File

@ -81,10 +81,10 @@ LOG_COLORS = {
# mypy doesn't know about this, so we need to ignore it.
VDEBUG_LEVEL = 9
logging.addLevelName(VDEBUG_LEVEL, 'VDEBUG')
logging.VDEBUG = VDEBUG_LEVEL # type: ignore
logging.VDEBUG = VDEBUG_LEVEL # type: ignore[attr-defined]
LOG_LEVELS = {
'VDEBUG': logging.VDEBUG, # type: ignore
'VDEBUG': logging.VDEBUG, # type: ignore[attr-defined]
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
@ -109,7 +109,7 @@ def vdebug(self: logging.Logger,
# pylint: enable=protected-access
logging.Logger.vdebug = vdebug # type: ignore
logging.Logger.vdebug = vdebug # type: ignore[attr-defined]
# The different loggers used.
@ -216,7 +216,7 @@ def init_log(args: argparse.Namespace) -> None:
root.setLevel(logging.NOTSET)
logging.captureWarnings(True)
_init_py_warnings()
QtCore.qInstallMessageHandler(qt_message_handler) # type: ignore
QtCore.qInstallMessageHandler(qt_message_handler)
_log_inited = True
@ -278,7 +278,7 @@ def _init_handlers(
level, color, force_color, json_logging)
if sys.stderr is None:
console_handler = None # type: ignore
console_handler = None # type: ignore[unreachable]
else:
strip = False if force_color else None
if use_colorama:
@ -337,14 +337,17 @@ def _init_formatters(
use_colors=False)
html_formatter = HTMLFormatter(EXTENDED_FMT_HTML, DATEFMT,
log_colors=LOG_COLORS)
use_colorama = False
if sys.stderr is None:
return None, ram_formatter, html_formatter, False # type: ignore
console_formatter = None # type: ignore[unreachable]
return console_formatter, ram_formatter, html_formatter, use_colorama
if json_logging:
json_formatter = JSONFormatter()
return json_formatter, ram_formatter, html_formatter, False
return json_formatter, ram_formatter, html_formatter, use_colorama
use_colorama = False
color_supported = os.name == 'posix' or colorama
if color_supported and (sys.stderr.isatty() or force_color) and color:
@ -481,13 +484,13 @@ def qt_message_handler(msg_type: QtCore.QtMsgType,
level = qt_to_logging[msg_type]
if context.function is None:
func = 'none' # type: ignore
func = 'none' # type: ignore[unreachable]
elif ':' in context.function:
func = '"{}"'.format(context.function)
else:
func = context.function
if (context.category is None or # type: ignore
if (context.category is None or # type: ignore[unreachable]
context.category == 'default'):
name = 'qt'
else:
@ -692,9 +695,10 @@ class HTMLFormatter(logging.Formatter):
record_clone.__dict__.update(self._colordict)
if record_clone.levelname in self._log_colors:
color = self._log_colors[record_clone.levelname]
record_clone.log_color = self._colordict[color] # type: ignore
color_str = self._colordict[color]
record_clone.log_color = color_str # type: ignore[attr-defined]
else:
record_clone.log_color = '' # type: ignore
record_clone.log_color = '' # type: ignore[attr-defined]
for field in ['msg', 'filename', 'funcName', 'levelname', 'module',
'name', 'pathname', 'processName', 'threadName']:
data = str(getattr(record_clone, field))

View File

@ -88,7 +88,7 @@ def _build_question(title: str,
mode: usertypes.PromptMode,
default: typing.Union[None, bool, str] = None,
abort_on: typing.Iterable[pyqtSignal] = (),
url: QUrl = None,
url: str = None,
option: bool = None) -> usertypes.Question:
"""Common function for ask/ask_async."""
question = usertypes.Question()

View File

@ -86,7 +86,7 @@ class ObjectRegistry(collections.UserDict):
if isinstance(obj, QObject):
func = functools.partial(self.on_destroyed, name)
obj.destroyed.connect(func) # type: ignore
obj.destroyed.connect(func) # type: ignore[attr-defined]
self._partial_objs[name] = func
super().__setitem__(name, obj)

View File

@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
# FIXME:typing Can we have less "# type: ignore" in here?
"""Misc. utilities related to Qt.
Module attributes:
@ -38,13 +36,16 @@ import typing
import pkg_resources
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QSaveFile, QT_VERSION_STR,
PYQT_VERSION_STR, QFileDevice, QObject)
PYQT_VERSION_STR, QObject, QUrl)
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication
try:
from PyQt5.QtWebKit import qWebKitVersion
except ImportError: # pragma: no cover
qWebKitVersion = None # type: ignore # noqa: N816
qWebKitVersion = None # type: ignore[assignment] # noqa: N816
if typing.TYPE_CHECKING:
from PyQt5.QtCore import QFileDevice
from qutebrowser.misc import objects
from qutebrowser.utils import usertypes
@ -63,13 +64,13 @@ MINVALS = {
class QtOSError(OSError):
"""An OSError triggered by a QFileDevice.
"""An OSError triggered by a QIODevice.
Attributes:
qt_errno: The error attribute of the given QFileDevice, if applicable.
"""
def __init__(self, dev: QFileDevice, msg: str = None) -> None:
def __init__(self, dev: QIODevice, msg: str = None) -> None:
if msg is None:
msg = dev.errorString()
@ -152,7 +153,16 @@ def check_overflow(arg: int, ctype: str, fatal: bool = True) -> int:
return arg
def ensure_valid(obj: QObject) -> None:
if typing.TYPE_CHECKING:
class Validatable(typing.Protocol):
"""An object with an isValid() method (e.g. QUrl)."""
def isValid(self) -> bool:
...
def ensure_valid(obj: 'Validatable') -> None:
"""Ensure a Qt object with an .isValid() method is valid."""
if not obj.isValid():
raise QtValueError(obj)
@ -172,7 +182,10 @@ def check_qdatastream(stream: QDataStream) -> None:
raise OSError(status_to_str[stream.status()])
def serialize(obj: QObject) -> QByteArray:
_QtSerializableType = typing.Union[QObject, QByteArray, QUrl]
def serialize(obj: _QtSerializableType) -> QByteArray:
"""Serialize an object into a QByteArray."""
data = QByteArray()
stream = QDataStream(data, QIODevice.WriteOnly)
@ -180,23 +193,25 @@ def serialize(obj: QObject) -> QByteArray:
return data
def deserialize(data: QByteArray, obj: QObject) -> None:
def deserialize(data: QByteArray, obj: _QtSerializableType) -> None:
"""Deserialize an object from a QByteArray."""
stream = QDataStream(data, QIODevice.ReadOnly)
deserialize_stream(stream, obj)
def serialize_stream(stream: QDataStream, obj: QObject) -> None:
def serialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None:
"""Serialize an object into a QDataStream."""
# pylint: disable=pointless-statement
check_qdatastream(stream)
stream << obj # pylint: disable=pointless-statement
stream << obj # type: ignore[operator]
check_qdatastream(stream)
def deserialize_stream(stream: QDataStream, obj: QObject) -> None:
def deserialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None:
"""Deserialize a QDataStream into an object."""
# pylint: disable=pointless-statement
check_qdatastream(stream)
stream >> obj # pylint: disable=pointless-statement
stream >> obj # type: ignore[operator]
check_qdatastream(stream)
@ -205,7 +220,7 @@ def savefile_open(
filename: str,
binary: bool = False,
encoding: str = 'utf-8'
) -> typing.Iterator[typing.Union['PyQIODevice', io.TextIOWrapper]]:
) -> typing.Iterator[typing.IO]:
"""Context manager to easily use a QSaveFile."""
f = QSaveFile(filename)
cancelled = False
@ -214,12 +229,12 @@ def savefile_open(
if not open_ok:
raise QtOSError(f)
dev = typing.cast(typing.IO[bytes], PyQIODevice(f))
if binary:
new_f = PyQIODevice(
f) # type: typing.Union[PyQIODevice, io.TextIOWrapper]
new_f = dev # type: typing.IO
else:
new_f = io.TextIOWrapper(PyQIODevice(f), # type: ignore
encoding=encoding)
new_f = io.TextIOWrapper(dev, encoding=encoding)
yield new_f
@ -335,28 +350,34 @@ class PyQIODevice(io.BufferedIOBase):
def readable(self) -> bool:
return self.dev.isReadable()
def readline(self, size: int = -1) -> QByteArray:
def readline(self, size: int = -1) -> bytes:
self._check_open()
self._check_readable()
if size < 0:
qt_size = 0 # no maximum size
elif size == 0:
return QByteArray()
return b''
else:
qt_size = size + 1 # Qt also counts the NUL byte
buf = None # type: typing.Union[QByteArray, bytes, None]
if self.dev.canReadLine():
buf = self.dev.readLine(qt_size)
elif size < 0:
buf = self.dev.readAll()
else:
if size < 0:
buf = self.dev.readAll()
else:
buf = self.dev.read(size)
buf = self.dev.read(size)
if buf is None:
raise QtOSError(self.dev)
return buf # type: ignore
if isinstance(buf, QByteArray):
# The type (bytes or QByteArray) seems to depend on what data we
# feed in...
buf = buf.data()
return buf
def seekable(self) -> bool:
return not self.dev.isSequential()
@ -369,26 +390,33 @@ class PyQIODevice(io.BufferedIOBase):
def writable(self) -> bool:
return self.dev.isWritable()
def write(self, data: str) -> int: # type: ignore
def write(self, data: typing.Union[bytes, bytearray]) -> int:
self._check_open()
self._check_writable()
num = self.dev.write(data) # type: ignore
num = self.dev.write(data)
if num == -1 or num < len(data):
raise QtOSError(self.dev)
return num
def read(self, size: typing.Optional[int] = None) -> QByteArray:
def read(self, size: typing.Optional[int] = None) -> bytes:
self._check_open()
self._check_readable()
buf = None # type: typing.Union[QByteArray, bytes, None]
if size in [None, -1]:
buf = self.dev.readAll()
else:
buf = self.dev.read(size) # type: ignore
assert size is not None
buf = self.dev.read(size)
if buf is None:
raise QtOSError(self.dev)
if isinstance(buf, QByteArray):
# The type (bytes or QByteArray) seems to depend on what data we
# feed in...
buf = buf.data()
return buf
@ -396,9 +424,9 @@ class QtValueError(ValueError):
"""Exception which gets raised by ensure_valid."""
def __init__(self, obj: QObject) -> None:
def __init__(self, obj: 'Validatable') -> None:
try:
self.reason = obj.errorString()
self.reason = obj.errorString() # type: ignore[attr-defined]
except AttributeError:
self.reason = None
err = "{} is not valid".format(obj)
@ -420,12 +448,13 @@ class EventLoop(QEventLoop):
def exec_(
self,
flags: QEventLoop.ProcessEventsFlag = QEventLoop.AllEvents
flags: QEventLoop.ProcessEventsFlags =
typing.cast(QEventLoop.ProcessEventsFlags, QEventLoop.AllEvents)
) -> int:
"""Override exec_ to raise an exception when re-running."""
if self._executing:
raise AssertionError("Eventloop is already running!")
self._executing = True
status = super().exec_(flags) # type: ignore
status = super().exec_(flags)
self._executing = False
return status

View File

@ -68,7 +68,7 @@ def _unset_organization() -> typing.Iterator[None]:
qapp = QApplication.instance()
if qapp is not None:
orgname = qapp.organizationName()
qapp.setOrganizationName(None) # type: ignore
qapp.setOrganizationName(None) # type: ignore[arg-type]
try:
yield
finally:

View File

@ -125,9 +125,9 @@ def _get_search_url(txt: str) -> QUrl:
url = qurl_from_user_input(evaluated)
else:
url = qurl_from_user_input(config.val.url.searchengines[engine])
url.setPath(None) # type: ignore
url.setFragment(None) # type: ignore
url.setQuery(None) # type: ignore
url.setPath(None) # type: ignore[arg-type]
url.setFragment(None) # type: ignore[arg-type]
url.setQuery(None) # type: ignore[call-overload]
qtutils.ensure_valid(url)
return url
@ -519,17 +519,17 @@ def encoded_url(url: QUrl) -> str:
Args:
url: The url to encode as QUrl.
"""
return bytes(url.toEncoded()).decode('ascii')
return url.toEncoded().data().decode('ascii')
def file_url(path: str) -> QUrl:
def file_url(path: str) -> str:
"""Return a file:// url (as string) to the given local path.
Arguments:
path: The absolute path to the local file
"""
url = QUrl.fromLocalFile(path)
return url.toString(QUrl.FullyEncoded) # type: ignore
return url.toString(QUrl.FullyEncoded) # type: ignore[arg-type]
def data_url(mimetype: str, data: bytes) -> QUrl:
@ -551,13 +551,13 @@ def safe_display_string(qurl: QUrl) -> str:
"""
ensure_valid(qurl)
host = qurl.host(QUrl.FullyEncoded) # type: ignore
host = qurl.host(QUrl.FullyEncoded)
if '..' in host: # pragma: no cover
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-60364
return '(unparseable URL!) {}'.format(qurl.toDisplayString())
for part in host.split('.'):
url_host = qurl.host(QUrl.FullyDecoded) # type: ignore
url_host = qurl.host(QUrl.FullyDecoded)
if part.startswith('xn--') and host != url_host:
return '({}) {}'.format(host, qurl.toDisplayString())
@ -584,7 +584,7 @@ class InvalidProxyTypeError(Exception):
super().__init__("Invalid proxy type {}!".format(typ))
def proxy_from_url(url: QUrl) -> QNetworkProxy:
def proxy_from_url(url: QUrl) -> typing.Union[QNetworkProxy, pac.PACFetcher]:
"""Create a QNetworkProxy from QUrl and a proxy type.
Args:

View File

@ -91,7 +91,7 @@ class NeighborList(typing.Sequence[_T]):
self._mode = mode
self.fuzzyval = None # type: typing.Optional[int]
def __getitem__(self, key: int) -> _T: # type: ignore
def __getitem__(self, key: int) -> _T: # type: ignore[override]
return self._items[key]
def __len__(self) -> int:
@ -120,7 +120,8 @@ class NeighborList(typing.Sequence[_T]):
if items:
item = min(
items,
key=lambda tpl: abs(self.fuzzyval - tpl[1])) # type: ignore
key=lambda tpl:
abs(self.fuzzyval - tpl[1])) # type: ignore[operator]
else:
sorted_items = sorted(enumerate(self.items), key=lambda e: e[1])
idx = 0 if offset < 0 else -1

View File

@ -47,7 +47,7 @@ try:
CSafeDumper as YamlDumper)
YAML_C_EXT = True
except ImportError: # pragma: no cover
from yaml import (SafeLoader as YamlLoader, # type: ignore
from yaml import (SafeLoader as YamlLoader, # type: ignore[misc]
SafeDumper as YamlDumper)
YAML_C_EXT = False
@ -324,7 +324,7 @@ class FakeIOStream(io.TextIOBase):
def __init__(self, write_func: typing.Callable[[str], int]) -> None:
super().__init__()
self.write = write_func # type: ignore
self.write = write_func # type: ignore[assignment]
@contextlib.contextmanager
@ -338,16 +338,16 @@ def fake_io(write_func: typing.Callable[[str], int]) -> typing.Iterator[None]:
old_stderr = sys.stderr
fake_stderr = FakeIOStream(write_func)
fake_stdout = FakeIOStream(write_func)
sys.stderr = fake_stderr # type: ignore
sys.stdout = fake_stdout # type: ignore
sys.stderr = fake_stderr # type: ignore[assignment]
sys.stdout = fake_stdout # type: ignore[assignment]
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: # type: ignore
if sys.stdout is fake_stdout: # type: ignore[comparison-overlap]
sys.stdout = old_stdout
if sys.stderr is fake_stderr: # type: ignore
if sys.stderr is fake_stderr: # type: ignore[comparison-overlap]
sys.stderr = old_stderr

View File

@ -43,7 +43,7 @@ from PyQt5.QtWidgets import QApplication
try:
from PyQt5.QtWebKit import qWebKitVersion
except ImportError: # pragma: no cover
qWebKitVersion = None # type: ignore # noqa: N816
qWebKitVersion = None # type: ignore[assignment] # noqa: N816
import qutebrowser
from qutebrowser.utils import log, utils, standarddir, usertypes, message
@ -54,7 +54,7 @@ from qutebrowser.config import config
try:
from qutebrowser.browser.webengine import webenginesettings
except ImportError: # pragma: no cover
webenginesettings = None # type: ignore
webenginesettings = None # type: ignore[assignment]
@attr.s
@ -373,7 +373,7 @@ def _chromium_version() -> str:
and https://chromereleases.googleblog.com/
"""
if webenginesettings is None:
return 'unavailable' # type: ignore
return 'unavailable' # type: ignore[unreachable]
if webenginesettings.parsed_user_agent is None:
webenginesettings.init_user_agent()

View File

@ -44,7 +44,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
import qutebrowser
from scripts import utils
from scripts.dev import update_3rdparty
from scripts.dev import update_3rdparty, misc_checks
def call_script(name, *args, python=sys.executable):
@ -473,6 +473,10 @@ def main():
import github3 # pylint: disable=unused-import
read_github_token()
if not misc_checks.check_git():
utils.print_error("Refusing to do a release with a dirty git tree")
sys.exit(1)
if args.no_asciidoc:
os.makedirs(os.path.join('qutebrowser', 'html', 'doc'), exist_ok=True)
else:

View File

@ -162,7 +162,7 @@ def _get_configtypes():
inspect.isclass(e) and
# pylint: disable=protected-access
e not in [configtypes.BaseType, configtypes.MappingType,
configtypes._Numeric] and
configtypes._Numeric, configtypes.FontBase] and
# pylint: enable=protected-access
issubclass(e, configtypes.BaseType))
yield from inspect.getmembers(configtypes, predicate)

View File

@ -110,7 +110,7 @@ def _apply_platform_markers(config, item):
"https://bugreports.qt.io/browse/QTBUG-60673"),
('qtwebkit6021_xfail',
pytest.mark.xfail,
version.qWebKitVersion and # type: ignore
version.qWebKitVersion and # type: ignore[unreachable]
version.qWebKitVersion() == '602.1',
"Broken on WebKit 602.1")
]

View File

@ -315,7 +315,7 @@ def config_stub(stubs, monkeypatch, configdata_init, yaml_config_stub, qapp):
monkeypatch.setattr(config, 'cache', cache)
try:
configtypes.Font.set_defaults(None, '10pt')
configtypes.FontBase.set_defaults(None, '10pt')
except configexc.NoOptionError:
# Completion tests patch configdata so fonts.default_family is
# unavailable.

View File

@ -257,7 +257,7 @@ class FakeWebTab(browsertab.AbstractTab):
scroll_pos_perc=(0, 0),
load_status=usertypes.LoadStatus.success,
progress=0, can_go_back=None, can_go_forward=None):
super().__init__(win_id=0, private=False)
super().__init__(win_id=0, mode_manager=None, private=False)
self._load_status = load_status
self._title = title
self._url = url

View File

@ -40,8 +40,8 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
monkeypatch.setattr(config, 'key_instance', None)
monkeypatch.setattr(config, 'change_filters', [])
monkeypatch.setattr(configinit, '_init_errors', None)
monkeypatch.setattr(configtypes.Font, 'default_family', None)
monkeypatch.setattr(configtypes.Font, 'default_size', None)
monkeypatch.setattr(configtypes.FontBase, 'default_family', None)
monkeypatch.setattr(configtypes.FontBase, 'default_size', None)
yield
try:
objreg.delete('config-commands')

View File

@ -212,7 +212,7 @@ class TestAll:
"""
for _name, member in inspect.getmembers(configtypes, inspect.isclass):
if member in [configtypes.BaseType, configtypes.MappingType,
configtypes._Numeric]:
configtypes._Numeric, configtypes.FontBase]:
pass
elif (member is configtypes.List or
member is configtypes.ListOrValue):
@ -1480,7 +1480,7 @@ class TestFont:
klass().to_py(val)
def test_defaults_replacement(self, klass, monkeypatch):
configtypes.Font.set_defaults(['Terminus'], '23pt')
configtypes.FontBase.set_defaults(['Terminus'], '23pt')
if klass is configtypes.Font:
expected = '23pt Terminus'
elif klass is configtypes.QtFont:

View File

@ -195,7 +195,6 @@ deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-dev.txt
-r{toxinidir}/misc/requirements/requirements-tests.txt
-r{toxinidir}/misc/requirements/requirements-pyqt.txt
-r{toxinidir}/misc/requirements/requirements-mypy.txt
commands =
{envpython} -m mypy qutebrowser tests {posargs}