Merge branch 'upstream-stubs'
This commit is contained in:
commit
20ab854510
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
|
|
|||
43
mypy.ini
43
mypy.ini
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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))):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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_()
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:]]
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -25,4 +25,4 @@
|
|||
try:
|
||||
from PyQt5 import sip
|
||||
except ImportError:
|
||||
import sip # type: ignore
|
||||
import sip # type: ignore[import, no-redef]
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
1
tox.ini
1
tox.ini
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Reference in New Issue