parent
7226750363
commit
3a5241b642
@ -43,6 +43,7 @@ function-rgx=[a-z_][a-z0-9_]{2,50}$
|
|||||||
const-rgx=[A-Za-z_][A-Za-z0-9_]{0,30}$
|
const-rgx=[A-Za-z_][A-Za-z0-9_]{0,30}$
|
||||||
method-rgx=[a-z_][A-Za-z0-9_]{1,50}$
|
method-rgx=[a-z_][A-Za-z0-9_]{1,50}$
|
||||||
attr-rgx=[a-z_][a-z0-9_]{0,30}$
|
attr-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||||
|
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{1,30}|(__.*__))$
|
||||||
argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||||
docstring-min-length=3
|
docstring-min-length=3
|
||||||
@ -64,6 +65,7 @@ valid-metaclass-classmethod-first-arg=cls
|
|||||||
|
|
||||||
[TYPECHECK]
|
[TYPECHECK]
|
||||||
ignored-modules=PyQt5,PyQt5.QtWebKit
|
ignored-modules=PyQt5,PyQt5.QtWebKit
|
||||||
|
ignored-classes=_CountingAttr
|
||||||
|
|
||||||
[IMPORTS]
|
[IMPORTS]
|
||||||
# WORKAROUND
|
# WORKAROUND
|
||||||
|
@ -116,6 +116,7 @@ The following software and libraries are required to run qutebrowser:
|
|||||||
* http://jinja.pocoo.org/[jinja2]
|
* http://jinja.pocoo.org/[jinja2]
|
||||||
* http://pygments.org/[pygments]
|
* http://pygments.org/[pygments]
|
||||||
* http://pyyaml.org/wiki/PyYAML[PyYAML]
|
* http://pyyaml.org/wiki/PyYAML[PyYAML]
|
||||||
|
* http://www.attrs.org/[attrs]
|
||||||
|
|
||||||
The following libraries are optional:
|
The following libraries are optional:
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ Breaking changes
|
|||||||
- (TODO) New dependency on ruamel.yaml; dropped PyYAML dependency.
|
- (TODO) New dependency on ruamel.yaml; dropped PyYAML dependency.
|
||||||
- (TODO) The QtWebEngine backend is now used by default if available.
|
- (TODO) The QtWebEngine backend is now used by default if available.
|
||||||
- New dependency on the QtSql module and Qt sqlite support.
|
- New dependency on the QtSql module and Qt sqlite support.
|
||||||
|
- New dependency on the `attrs` Python module.
|
||||||
- New config system which ignores the old config file.
|
- New config system which ignores the old config file.
|
||||||
- The depedency on PyOpenGL (when using QtWebEngine) got removed. Note
|
- The depedency on PyOpenGL (when using QtWebEngine) got removed. Note
|
||||||
that PyQt5.QtOpenGL is still a dependency.
|
that PyQt5.QtOpenGL is still a dependency.
|
||||||
|
@ -4,3 +4,4 @@ pyPEG2
|
|||||||
PyYAML
|
PyYAML
|
||||||
colorama
|
colorama
|
||||||
cssutils
|
cssutils
|
||||||
|
attrs
|
||||||
|
@ -40,6 +40,7 @@ git+https://github.com/pallets/jinja.git
|
|||||||
git+https://github.com/pallets/markupsafe.git
|
git+https://github.com/pallets/markupsafe.git
|
||||||
hg+http://bitbucket.org/birkenfeld/pygments-main
|
hg+http://bitbucket.org/birkenfeld/pygments-main
|
||||||
hg+https://bitbucket.org/fdik/pypeg
|
hg+https://bitbucket.org/fdik/pypeg
|
||||||
|
git+https://github.com/python-attrs/attrs.git
|
||||||
|
|
||||||
# Fails to build:
|
# Fails to build:
|
||||||
# gcc: error: ext/_yaml.c: No such file or directory
|
# gcc: error: ext/_yaml.c: No such file or directory
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication
|
from PyQt5.QtWidgets import QWidget, QApplication
|
||||||
@ -82,6 +83,7 @@ TerminationStatus = usertypes.enum('TerminationStatus', [
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class TabData:
|
class TabData:
|
||||||
|
|
||||||
"""A simple namespace with a fixed set of attributes.
|
"""A simple namespace with a fixed set of attributes.
|
||||||
@ -97,13 +99,12 @@ class TabData:
|
|||||||
fullscreen: Whether the tab has a video shown fullscreen currently.
|
fullscreen: Whether the tab has a video shown fullscreen currently.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
keep_icon = attr.ib(False)
|
||||||
self.keep_icon = False
|
viewing_source = attr.ib(False)
|
||||||
self.viewing_source = False
|
inspector = attr.ib(None)
|
||||||
self.inspector = None
|
override_target = attr.ib(None)
|
||||||
self.override_target = None
|
pinned = attr.ib(False)
|
||||||
self.pinned = False
|
fullscreen = attr.ib(False)
|
||||||
self.fullscreen = False
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractAction:
|
class AbstractAction:
|
||||||
|
@ -26,6 +26,7 @@ import re
|
|||||||
import html
|
import html
|
||||||
from string import ascii_lowercase
|
from string import ascii_lowercase
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSlot, QObject, Qt, QUrl
|
from PyQt5.QtCore import pyqtSlot, QObject, Qt, QUrl
|
||||||
from PyQt5.QtWidgets import QLabel
|
from PyQt5.QtWidgets import QLabel
|
||||||
|
|
||||||
@ -131,6 +132,7 @@ class HintLabel(QLabel):
|
|||||||
self.deleteLater()
|
self.deleteLater()
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class HintContext:
|
class HintContext:
|
||||||
|
|
||||||
"""Context namespace used for hinting.
|
"""Context namespace used for hinting.
|
||||||
@ -158,19 +160,18 @@ class HintContext:
|
|||||||
group: The group of web elements to hint.
|
group: The group of web elements to hint.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
all_labels = attr.ib(attr.Factory(list))
|
||||||
self.all_labels = []
|
labels = attr.ib(attr.Factory(dict))
|
||||||
self.labels = {}
|
target = attr.ib(None)
|
||||||
self.target = None
|
baseurl = attr.ib(None)
|
||||||
self.baseurl = None
|
to_follow = attr.ib(None)
|
||||||
self.to_follow = None
|
rapid = attr.ib(False)
|
||||||
self.rapid = False
|
add_history = attr.ib(False)
|
||||||
self.add_history = False
|
filterstr = attr.ib(None)
|
||||||
self.filterstr = None
|
args = attr.ib(attr.Factory(list))
|
||||||
self.args = []
|
tab = attr.ib(None)
|
||||||
self.tab = None
|
group = attr.ib(None)
|
||||||
self.group = None
|
hint_mode = attr.ib(None)
|
||||||
self.hint_mode = None
|
|
||||||
|
|
||||||
def get_args(self, urlstr):
|
def get_args(self, urlstr):
|
||||||
"""Get the arguments, with {hint-url} replaced by the given URL."""
|
"""Get the arguments, with {hint-url} replaced by the given URL."""
|
||||||
@ -389,6 +390,7 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
"""Clean up after hinting."""
|
"""Clean up after hinting."""
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
for label in self._context.all_labels:
|
for label in self._context.all_labels:
|
||||||
label.cleanup()
|
label.cleanup()
|
||||||
|
|
||||||
@ -795,6 +797,7 @@ class HintManager(QObject):
|
|||||||
log.hints.debug("Filtering hints on {!r}".format(filterstr))
|
log.hints.debug("Filtering hints on {!r}".format(filterstr))
|
||||||
|
|
||||||
visible = []
|
visible = []
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
for label in self._context.all_labels:
|
for label in self._context.all_labels:
|
||||||
try:
|
try:
|
||||||
if self._filter_matches(filterstr, str(label.elem)):
|
if self._filter_matches(filterstr, str(label.elem)):
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
import io
|
import io
|
||||||
import shutil
|
import shutil
|
||||||
import functools
|
import functools
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer
|
||||||
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
|
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
|
||||||
|
|
||||||
@ -34,7 +34,11 @@ from qutebrowser.browser.webkit import http
|
|||||||
from qutebrowser.browser.webkit.network import networkmanager
|
from qutebrowser.browser.webkit.network import networkmanager
|
||||||
|
|
||||||
|
|
||||||
_RetryInfo = collections.namedtuple('_RetryInfo', ['request', 'manager'])
|
@attr.s
|
||||||
|
class _RetryInfo:
|
||||||
|
|
||||||
|
request = attr.ib()
|
||||||
|
manager = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class DownloadItem(downloads.AbstractDownloadItem):
|
class DownloadItem(downloads.AbstractDownloadItem):
|
||||||
|
@ -25,7 +25,6 @@ import io
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import collections
|
|
||||||
import uuid
|
import uuid
|
||||||
import email.policy
|
import email.policy
|
||||||
import email.generator
|
import email.generator
|
||||||
@ -34,15 +33,21 @@ import email.mime.multipart
|
|||||||
import email.message
|
import email.message
|
||||||
import quopri
|
import quopri
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
from qutebrowser.browser import downloads
|
from qutebrowser.browser import downloads
|
||||||
from qutebrowser.browser.webkit import webkitelem
|
from qutebrowser.browser.webkit import webkitelem
|
||||||
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
|
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
|
||||||
|
|
||||||
_File = collections.namedtuple('_File',
|
|
||||||
['content', 'content_type', 'content_location',
|
@attr.s
|
||||||
'transfer_encoding'])
|
class _File:
|
||||||
|
|
||||||
|
content = attr.ib()
|
||||||
|
content_type = attr.ib()
|
||||||
|
content_location = attr.ib()
|
||||||
|
transfer_encoding = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
_CSS_URL_PATTERNS = [re.compile(x) for x in [
|
_CSS_URL_PATTERNS = [re.compile(x) for x in [
|
||||||
@ -174,7 +179,7 @@ class MHTMLWriter:
|
|||||||
root_content: The root content as bytes.
|
root_content: The root content as bytes.
|
||||||
content_location: The url of the page as str.
|
content_location: The url of the page as str.
|
||||||
content_type: The MIME-type of the root content as str.
|
content_type: The MIME-type of the root content as str.
|
||||||
_files: Mapping of location->_File namedtuple.
|
_files: Mapping of location->_File object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, root_content, content_location, content_type):
|
def __init__(self, root_content, content_location, content_type):
|
||||||
|
@ -24,6 +24,7 @@ import collections
|
|||||||
import netrc
|
import netrc
|
||||||
import html
|
import html
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QCoreApplication, QUrl,
|
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QCoreApplication, QUrl,
|
||||||
QByteArray)
|
QByteArray)
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
|
||||||
@ -37,10 +38,19 @@ from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
|||||||
|
|
||||||
|
|
||||||
HOSTBLOCK_ERROR_STRING = '%HOSTBLOCK%'
|
HOSTBLOCK_ERROR_STRING = '%HOSTBLOCK%'
|
||||||
ProxyId = collections.namedtuple('ProxyId', 'type, hostname, port')
|
|
||||||
_proxy_auth_cache = {}
|
_proxy_auth_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class ProxyId:
|
||||||
|
|
||||||
|
"""Information identifying a proxy server."""
|
||||||
|
|
||||||
|
type = attr.ib()
|
||||||
|
hostname = attr.ib()
|
||||||
|
port = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def _is_secure_cipher(cipher):
|
def _is_secure_cipher(cipher):
|
||||||
"""Check if a given SSL cipher (hopefully) isn't broken yet."""
|
"""Check if a given SSL cipher (hopefully) isn't broken yet."""
|
||||||
tokens = [e.upper() for e in cipher.name().split('-')]
|
tokens = [e.upper() for e in cipher.name().split('-')]
|
||||||
|
@ -19,11 +19,11 @@
|
|||||||
|
|
||||||
"""pyPEG parsing for the RFC 6266 (Content-Disposition) header."""
|
"""pyPEG parsing for the RFC 6266 (Content-Disposition) header."""
|
||||||
|
|
||||||
import collections
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import attr
|
||||||
import pypeg2 as peg
|
import pypeg2 as peg
|
||||||
|
|
||||||
from qutebrowser.utils import utils
|
from qutebrowser.utils import utils
|
||||||
@ -210,7 +210,13 @@ class ContentDispositionValue:
|
|||||||
peg.optional(';'))
|
peg.optional(';'))
|
||||||
|
|
||||||
|
|
||||||
LangTagged = collections.namedtuple('LangTagged', ['string', 'langtag'])
|
@attr.s
|
||||||
|
class LangTagged:
|
||||||
|
|
||||||
|
"""A string with an associated language."""
|
||||||
|
|
||||||
|
string = attr.ib()
|
||||||
|
langtag = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
@ -24,43 +24,32 @@ import collections
|
|||||||
import traceback
|
import traceback
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
from qutebrowser.commands import cmdexc, argparser
|
from qutebrowser.commands import cmdexc, argparser
|
||||||
from qutebrowser.utils import (log, utils, message, docutils, objreg,
|
from qutebrowser.utils import log, message, docutils, objreg, usertypes
|
||||||
usertypes)
|
|
||||||
from qutebrowser.utils import debug as debug_utils
|
from qutebrowser.utils import debug as debug_utils
|
||||||
from qutebrowser.misc import objects
|
from qutebrowser.misc import objects
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class ArgInfo:
|
class ArgInfo:
|
||||||
|
|
||||||
"""Information about an argument."""
|
"""Information about an argument."""
|
||||||
|
|
||||||
def __init__(self, win_id=False, count=False, hide=False, metavar=None,
|
win_id = attr.ib(False)
|
||||||
flag=None, completion=None, choices=None):
|
count = attr.ib(False)
|
||||||
if win_id and count:
|
hide = attr.ib(False)
|
||||||
|
metavar = attr.ib(None)
|
||||||
|
flag = attr.ib(None)
|
||||||
|
completion = attr.ib(None)
|
||||||
|
choices = attr.ib(None)
|
||||||
|
|
||||||
|
@win_id.validator
|
||||||
|
@count.validator
|
||||||
|
def _validate_exclusive(self, _attr, _value):
|
||||||
|
if self.win_id and self.count:
|
||||||
raise TypeError("Argument marked as both count/win_id!")
|
raise TypeError("Argument marked as both count/win_id!")
|
||||||
self.win_id = win_id
|
|
||||||
self.count = count
|
|
||||||
self.flag = flag
|
|
||||||
self.hide = hide
|
|
||||||
self.metavar = metavar
|
|
||||||
self.completion = completion
|
|
||||||
self.choices = choices
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return (self.win_id == other.win_id and
|
|
||||||
self.count == other.count and
|
|
||||||
self.flag == other.flag and
|
|
||||||
self.hide == other.hide and
|
|
||||||
self.metavar == other.metavar and
|
|
||||||
self.completion == other.completion and
|
|
||||||
self.choices == other.choices)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return utils.get_repr(self, win_id=self.win_id, count=self.count,
|
|
||||||
flag=self.flag, hide=self.hide,
|
|
||||||
metavar=self.metavar, completion=self.completion,
|
|
||||||
choices=self.choices, constructor=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
|
|
||||||
"""Module containing command managers (SearchRunner and CommandRunner)."""
|
"""Module containing command managers (SearchRunner and CommandRunner)."""
|
||||||
|
|
||||||
import collections
|
|
||||||
import traceback
|
import traceback
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSlot, QUrl, QObject
|
from PyQt5.QtCore import pyqtSlot, QUrl, QObject
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
@ -31,10 +31,19 @@ from qutebrowser.utils import message, objreg, qtutils, usertypes, utils
|
|||||||
from qutebrowser.misc import split
|
from qutebrowser.misc import split
|
||||||
|
|
||||||
|
|
||||||
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline'])
|
|
||||||
last_command = {}
|
last_command = {}
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class ParseResult:
|
||||||
|
|
||||||
|
"""The result of parsing a commandline."""
|
||||||
|
|
||||||
|
cmd = attr.ib()
|
||||||
|
args = attr.ib()
|
||||||
|
cmdline = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def _current_url(tabbed_browser):
|
def _current_url(tabbed_browser):
|
||||||
"""Convenience method to get the current url."""
|
"""Convenience method to get the current url."""
|
||||||
try:
|
try:
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
|
|
||||||
"""Completer attached to a CompletionView."""
|
"""Completer attached to a CompletionView."""
|
||||||
|
|
||||||
import collections
|
import attr
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, QObject, QTimer
|
from PyQt5.QtCore import pyqtSlot, QObject, QTimer
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
@ -29,9 +28,13 @@ from qutebrowser.utils import log, utils, debug
|
|||||||
from qutebrowser.completion.models import miscmodels
|
from qutebrowser.completion.models import miscmodels
|
||||||
|
|
||||||
|
|
||||||
# Context passed into all completion functions
|
@attr.s
|
||||||
CompletionInfo = collections.namedtuple('CompletionInfo',
|
class CompletionInfo:
|
||||||
['config', 'keyconf'])
|
|
||||||
|
"""Context passed into all completion functions."""
|
||||||
|
|
||||||
|
config = attr.ib()
|
||||||
|
keyconf = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Completer(QObject):
|
class Completer(QObject):
|
||||||
@ -130,7 +133,9 @@ class Completer(QObject):
|
|||||||
return [], '', []
|
return [], '', []
|
||||||
parser = runners.CommandParser()
|
parser = runners.CommandParser()
|
||||||
result = parser.parse(text, fallback=True, keep=True)
|
result = parser.parse(text, fallback=True, keep=True)
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
parts = [x for x in result.cmdline if x]
|
parts = [x for x in result.cmdline if x]
|
||||||
|
# pylint: enable=not-an-iterable
|
||||||
pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
|
pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
|
||||||
pos = min(pos, len(text)) # Qt treats 2-byte UTF-16 chars as 2 chars
|
pos = min(pos, len(text)) # Qt treats 2-byte UTF-16 chars as 2 chars
|
||||||
log.completion.debug('partitioning {} around position {}'.format(parts,
|
log.completion.debug('partitioning {} around position {}'.format(parts,
|
||||||
|
@ -24,16 +24,29 @@ Module attributes:
|
|||||||
DATA: A dict of Option objects after init() has been called.
|
DATA: A dict of Option objects after init() has been called.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import collections
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
import attr
|
||||||
from qutebrowser.config import configtypes
|
from qutebrowser.config import configtypes
|
||||||
from qutebrowser.utils import usertypes, qtutils, utils
|
from qutebrowser.utils import usertypes, qtutils, utils
|
||||||
|
|
||||||
DATA = None
|
DATA = None
|
||||||
Option = collections.namedtuple('Option', ['name', 'typ', 'default',
|
|
||||||
'backends', 'raw_backends',
|
|
||||||
'description'])
|
@attr.s
|
||||||
|
class Option:
|
||||||
|
|
||||||
|
"""Description of an Option in the config.
|
||||||
|
|
||||||
|
Note that this is just an option which exists, with no value associated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = attr.ib()
|
||||||
|
typ = attr.ib()
|
||||||
|
default = attr.ib()
|
||||||
|
backends = attr.ib()
|
||||||
|
raw_backends = attr.ib()
|
||||||
|
description = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def _raise_invalid_node(name, what, node):
|
def _raise_invalid_node(name, what, node):
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
"""Exceptions related to config parsing."""
|
"""Exceptions related to config parsing."""
|
||||||
|
|
||||||
from qutebrowser.utils import utils, jinja
|
import attr
|
||||||
|
|
||||||
|
from qutebrowser.utils import jinja
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
@ -74,6 +76,7 @@ class NoOptionError(Error):
|
|||||||
self.option = option
|
self.option = option
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class ConfigErrorDesc:
|
class ConfigErrorDesc:
|
||||||
|
|
||||||
"""A description of an error happening while reading the config.
|
"""A description of an error happening while reading the config.
|
||||||
@ -84,13 +87,9 @@ class ConfigErrorDesc:
|
|||||||
traceback: The formatted traceback of the exception.
|
traceback: The formatted traceback of the exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text, exception, traceback=None):
|
text = attr.ib()
|
||||||
self.text = text
|
exception = attr.ib()
|
||||||
self.exception = exception
|
traceback = attr.ib(None)
|
||||||
self.traceback = traceback
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return utils.get_repr(self, text=self.text, exception=self.exception)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}: {}'.format(self.text, self.exception)
|
return '{}: {}'.format(self.text, self.exception)
|
||||||
|
@ -47,13 +47,13 @@ import html
|
|||||||
import codecs
|
import codecs
|
||||||
import os.path
|
import os.path
|
||||||
import itertools
|
import itertools
|
||||||
import collections
|
|
||||||
import warnings
|
import warnings
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import operator
|
import operator
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import attr
|
||||||
import yaml
|
import yaml
|
||||||
from PyQt5.QtCore import QUrl, Qt
|
from PyQt5.QtCore import QUrl, Qt
|
||||||
from PyQt5.QtGui import QColor, QFont
|
from PyQt5.QtGui import QColor, QFont
|
||||||
@ -1360,8 +1360,15 @@ class FuzzyUrl(BaseType):
|
|||||||
raise configexc.ValidationError(value, str(e))
|
raise configexc.ValidationError(value, str(e))
|
||||||
|
|
||||||
|
|
||||||
PaddingValues = collections.namedtuple('PaddingValues', ['top', 'bottom',
|
@attr.s
|
||||||
'left', 'right'])
|
class PaddingValues:
|
||||||
|
|
||||||
|
"""Four padding values."""
|
||||||
|
|
||||||
|
top = attr.ib()
|
||||||
|
bottom = attr.ib()
|
||||||
|
left = attr.ib()
|
||||||
|
right = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Padding(Dict):
|
class Padding(Dict):
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ from qutebrowser.utils import usertypes, log, objreg, utils
|
|||||||
from qutebrowser.misc import objects
|
from qutebrowser.misc import objects
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True)
|
||||||
class KeyEvent:
|
class KeyEvent:
|
||||||
|
|
||||||
"""A small wrapper over a QKeyEvent storing its data.
|
"""A small wrapper over a QKeyEvent storing its data.
|
||||||
@ -44,18 +46,13 @@ class KeyEvent:
|
|||||||
text: A string (QKeyEvent::text).
|
text: A string (QKeyEvent::text).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, keyevent):
|
key = attr.ib()
|
||||||
self.key = keyevent.key()
|
text = attr.ib()
|
||||||
self.text = keyevent.text()
|
|
||||||
|
|
||||||
def __repr__(self):
|
@classmethod
|
||||||
return utils.get_repr(self, key=self.key, text=self.text)
|
def from_event(cls, event):
|
||||||
|
"""Initialize a KeyEvent from a QKeyEvent."""
|
||||||
def __eq__(self, other):
|
return cls(event.key(), event.text())
|
||||||
return self.key == other.key and self.text == other.text
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash((self.key, self.text))
|
|
||||||
|
|
||||||
|
|
||||||
class NotInModeError(Exception):
|
class NotInModeError(Exception):
|
||||||
@ -179,7 +176,7 @@ class ModeManager(QObject):
|
|||||||
filter_this = True
|
filter_this = True
|
||||||
|
|
||||||
if not filter_this:
|
if not filter_this:
|
||||||
self._releaseevents_to_pass.add(KeyEvent(event))
|
self._releaseevents_to_pass.add(KeyEvent.from_event(event))
|
||||||
|
|
||||||
if curmode != usertypes.KeyMode.insert:
|
if curmode != usertypes.KeyMode.insert:
|
||||||
focus_widget = QApplication.instance().focusWidget()
|
focus_widget = QApplication.instance().focusWidget()
|
||||||
@ -201,7 +198,7 @@ class ModeManager(QObject):
|
|||||||
True if event should be filtered, False otherwise.
|
True if event should be filtered, False otherwise.
|
||||||
"""
|
"""
|
||||||
# handle like matching KeyPress
|
# handle like matching KeyPress
|
||||||
keyevent = KeyEvent(event)
|
keyevent = KeyEvent.from_event(event)
|
||||||
if keyevent in self._releaseevents_to_pass:
|
if keyevent in self._releaseevents_to_pass:
|
||||||
self._releaseevents_to_pass.remove(keyevent)
|
self._releaseevents_to_pass.remove(keyevent)
|
||||||
filter_this = False
|
filter_this = False
|
||||||
|
@ -23,6 +23,7 @@ import os.path
|
|||||||
import html
|
import html
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
import attr
|
||||||
import sip
|
import sip
|
||||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex,
|
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex,
|
||||||
QItemSelectionModel, QObject, QEventLoop)
|
QItemSelectionModel, QObject, QEventLoop)
|
||||||
@ -39,7 +40,13 @@ from qutebrowser.commands import cmdutils, cmdexc
|
|||||||
prompt_queue = None
|
prompt_queue = None
|
||||||
|
|
||||||
|
|
||||||
AuthTuple = collections.namedtuple('AuthTuple', ['user', 'password'])
|
@attr.s
|
||||||
|
class AuthInfo:
|
||||||
|
|
||||||
|
"""Authentication info returned by a prompt."""
|
||||||
|
|
||||||
|
user = attr.ib()
|
||||||
|
password = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
@ -750,7 +757,7 @@ class AuthenticationPrompt(_BasePrompt):
|
|||||||
"username:password, but {} was given".format(
|
"username:password, but {} was given".format(
|
||||||
value))
|
value))
|
||||||
username, password = value.split(':', maxsplit=1)
|
username, password = value.split(':', maxsplit=1)
|
||||||
self.question.answer = AuthTuple(username, password)
|
self.question.answer = AuthInfo(username, password)
|
||||||
return True
|
return True
|
||||||
elif self._user_lineedit.hasFocus():
|
elif self._user_lineedit.hasFocus():
|
||||||
# Earlier, tab was bound to :prompt-accept, so to still support
|
# Earlier, tab was bound to :prompt-accept, so to still support
|
||||||
@ -758,8 +765,8 @@ class AuthenticationPrompt(_BasePrompt):
|
|||||||
self._password_lineedit.setFocus()
|
self._password_lineedit.setFocus()
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.question.answer = AuthTuple(self._user_lineedit.text(),
|
self.question.answer = AuthInfo(self._user_lineedit.text(),
|
||||||
self._password_lineedit.text())
|
self._password_lineedit.text())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def item_focus(self, which):
|
def item_focus(self, which):
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
"""The main statusbar widget."""
|
"""The main statusbar widget."""
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
|
||||||
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
|
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ from qutebrowser.mainwindow.statusbar import (backforward, command, progress,
|
|||||||
from qutebrowser.mainwindow.statusbar import text as textwidget
|
from qutebrowser.mainwindow.statusbar import text as textwidget
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class ColorFlags:
|
class ColorFlags:
|
||||||
|
|
||||||
"""Flags which change the appearance of the statusbar.
|
"""Flags which change the appearance of the statusbar.
|
||||||
@ -44,13 +46,11 @@ class ColorFlags:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection'])
|
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection'])
|
||||||
|
prompt = attr.ib(False)
|
||||||
def __init__(self):
|
insert = attr.ib(False)
|
||||||
self.prompt = False
|
command = attr.ib(False)
|
||||||
self.insert = False
|
caret = attr.ib(CaretMode.off)
|
||||||
self.command = False
|
private = attr.ib(False)
|
||||||
self.caret = self.CaretMode.off
|
|
||||||
self.private = False
|
|
||||||
|
|
||||||
def to_stringlist(self):
|
def to_stringlist(self):
|
||||||
"""Get a string list of set flags used in the stylesheet.
|
"""Get a string list of set flags used in the stylesheet.
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
"""The main tabbed browser widget."""
|
"""The main tabbed browser widget."""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtWidgets import QSizePolicy
|
from PyQt5.QtWidgets import QSizePolicy
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
@ -34,8 +34,15 @@ from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg,
|
|||||||
urlutils, message, jinja)
|
urlutils, message, jinja)
|
||||||
|
|
||||||
|
|
||||||
UndoEntry = collections.namedtuple('UndoEntry',
|
@attr.s
|
||||||
['url', 'history', 'index', 'pinned'])
|
class UndoEntry:
|
||||||
|
|
||||||
|
"""Information needed for :undo."""
|
||||||
|
|
||||||
|
url = attr.ib()
|
||||||
|
history = attr.ib()
|
||||||
|
index = attr.ib()
|
||||||
|
pinned = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class TabDeletedError(Exception):
|
class TabDeletedError(Exception):
|
||||||
@ -64,7 +71,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
_tab_insert_idx_left: Where to insert a new tab with
|
_tab_insert_idx_left: Where to insert a new tab with
|
||||||
tabs.new_tab_position set to 'prev'.
|
tabs.new_tab_position set to 'prev'.
|
||||||
_tab_insert_idx_right: Same as above, for 'next'.
|
_tab_insert_idx_right: Same as above, for 'next'.
|
||||||
_undo_stack: List of UndoEntry namedtuples of closed tabs.
|
_undo_stack: List of UndoEntry objects of closed tabs.
|
||||||
shutting_down: Whether we're currently shutting down.
|
shutting_down: Whether we're currently shutting down.
|
||||||
_local_marks: Jump markers local to each page
|
_local_marks: Jump markers local to each page
|
||||||
_global_marks: Jump markers used across all pages
|
_global_marks: Jump markers used across all pages
|
||||||
@ -352,16 +359,16 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
use_current_tab = (only_one_tab_open and no_history and
|
use_current_tab = (only_one_tab_open and no_history and
|
||||||
last_close_url_used)
|
last_close_url_used)
|
||||||
|
|
||||||
url, history_data, idx, pinned = self._undo_stack.pop()
|
entry = self._undo_stack.pop()
|
||||||
|
|
||||||
if use_current_tab:
|
if use_current_tab:
|
||||||
self.openurl(url, newtab=False)
|
self.openurl(entry.url, newtab=False)
|
||||||
newtab = self.widget(0)
|
newtab = self.widget(0)
|
||||||
else:
|
else:
|
||||||
newtab = self.tabopen(url, background=False, idx=idx)
|
newtab = self.tabopen(entry.url, background=False, idx=entry.index)
|
||||||
|
|
||||||
newtab.history.deserialize(history_data)
|
newtab.history.deserialize(entry.history)
|
||||||
self.set_tab_pinned(newtab, pinned)
|
self.set_tab_pinned(newtab, entry.pinned)
|
||||||
|
|
||||||
@pyqtSlot('QUrl', bool)
|
@pyqtSlot('QUrl', bool)
|
||||||
def openurl(self, url, newtab):
|
def openurl(self, url, newtab):
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
"""The tab widget used for TabbedBrowser from browser.py."""
|
"""The tab widget used for TabbedBrowser from browser.py."""
|
||||||
|
|
||||||
import collections
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint,
|
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint,
|
||||||
QTimer, QUrl)
|
QTimer, QUrl)
|
||||||
from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle,
|
from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle,
|
||||||
@ -592,8 +592,17 @@ class TabBar(QTabBar):
|
|||||||
tabbed_browser.wheelEvent(e)
|
tabbed_browser.wheelEvent(e)
|
||||||
|
|
||||||
|
|
||||||
# Used by TabBarStyle._tab_layout().
|
@attr.s
|
||||||
Layouts = collections.namedtuple('Layouts', ['text', 'icon', 'indicator'])
|
class Layouts:
|
||||||
|
|
||||||
|
"""Layout information for tab.
|
||||||
|
|
||||||
|
Used by TabBarStyle._tab_layout().
|
||||||
|
"""
|
||||||
|
|
||||||
|
text = attr.ib()
|
||||||
|
icon = attr.ib()
|
||||||
|
indicator = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class TabBarStyle(QCommonStyle):
|
class TabBarStyle(QCommonStyle):
|
||||||
@ -765,7 +774,7 @@ class TabBarStyle(QCommonStyle):
|
|||||||
opt: QStyleOptionTab
|
opt: QStyleOptionTab
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
A Layout namedtuple with two QRects.
|
A Layout object with two QRects.
|
||||||
"""
|
"""
|
||||||
padding = config.val.tabs.padding
|
padding = config.val.tabs.padding
|
||||||
indicator_padding = config.val.tabs.indicator_padding
|
indicator_padding = config.val.tabs.indicator_padding
|
||||||
|
@ -27,13 +27,13 @@ import signal
|
|||||||
import functools
|
import functools
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import os.path
|
import os.path
|
||||||
import collections
|
|
||||||
try:
|
try:
|
||||||
# WORKAROUND for segfaults when using pdb in pytest for some reason...
|
# WORKAROUND for segfaults when using pdb in pytest for some reason...
|
||||||
import readline # pylint: disable=unused-import
|
import readline # pylint: disable=unused-import
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QObject,
|
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QObject,
|
||||||
QSocketNotifier, QTimer, QUrl)
|
QSocketNotifier, QTimer, QUrl)
|
||||||
from PyQt5.QtWidgets import QApplication, QDialog
|
from PyQt5.QtWidgets import QApplication, QDialog
|
||||||
@ -43,8 +43,14 @@ from qutebrowser.misc import earlyinit, crashdialog
|
|||||||
from qutebrowser.utils import usertypes, standarddir, log, objreg, debug
|
from qutebrowser.utils import usertypes, standarddir, log, objreg, debug
|
||||||
|
|
||||||
|
|
||||||
ExceptionInfo = collections.namedtuple('ExceptionInfo',
|
@attr.s
|
||||||
'pages, cmd_history, objects')
|
class ExceptionInfo:
|
||||||
|
|
||||||
|
"""Information stored when there was an exception."""
|
||||||
|
|
||||||
|
pages = attr.ib()
|
||||||
|
cmd_history = attr.ib()
|
||||||
|
objects = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
# Used by mainwindow.py to skip confirm questions on crashes
|
# Used by mainwindow.py to skip confirm questions on crashes
|
||||||
@ -172,7 +178,7 @@ class CrashHandler(QObject):
|
|||||||
"""Get info needed for the exception hook/dialog.
|
"""Get info needed for the exception hook/dialog.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
An ExceptionInfo namedtuple.
|
An ExceptionInfo object.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
pages = self._recover_pages(forgiving=True)
|
pages = self._recover_pages(forgiving=True)
|
||||||
|
@ -261,6 +261,9 @@ def check_libraries():
|
|||||||
"http://pyyaml.org/download/pyyaml/ (py3.4) "
|
"http://pyyaml.org/download/pyyaml/ (py3.4) "
|
||||||
"or Install via pip.",
|
"or Install via pip.",
|
||||||
pip="PyYAML"),
|
pip="PyYAML"),
|
||||||
|
'attr':
|
||||||
|
_missing_str("attrs",
|
||||||
|
pip="attrs"),
|
||||||
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
|
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
|
||||||
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
|
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
|
||||||
'PyQt5.QtOpenGL': _missing_str("PyQt5.QtOpenGL"),
|
'PyQt5.QtOpenGL': _missing_str("PyQt5.QtOpenGL"),
|
||||||
|
@ -31,6 +31,7 @@ import contextlib
|
|||||||
import socket
|
import socket
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import Qt, QUrl
|
from PyQt5.QtCore import Qt, QUrl
|
||||||
from PyQt5.QtGui import QKeySequence, QColor, QClipboard, QDesktopServices
|
from PyQt5.QtGui import QKeySequence, QColor, QClipboard, QDesktopServices
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
@ -409,6 +410,7 @@ def keyevent_to_string(e):
|
|||||||
return normalize_keystr('+'.join(parts))
|
return normalize_keystr('+'.join(parts))
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(repr=False)
|
||||||
class KeyInfo:
|
class KeyInfo:
|
||||||
|
|
||||||
"""Stores information about a key, like used in a QKeyEvent.
|
"""Stores information about a key, like used in a QKeyEvent.
|
||||||
@ -419,10 +421,9 @@ class KeyInfo:
|
|||||||
text: str
|
text: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, key, modifiers, text):
|
key = attr.ib()
|
||||||
self.key = key
|
modifiers = attr.ib()
|
||||||
self.modifiers = modifiers
|
text = attr.ib()
|
||||||
self.text = text
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.modifiers is None:
|
if self.modifiers is None:
|
||||||
@ -434,10 +435,6 @@ class KeyInfo:
|
|||||||
key=debug.qenum_key(Qt, self.key),
|
key=debug.qenum_key(Qt, self.key),
|
||||||
modifiers=modifiers, text=self.text)
|
modifiers=modifiers, text=self.text)
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return (self.key == other.key and self.modifiers == other.modifiers and
|
|
||||||
self.text == other.text)
|
|
||||||
|
|
||||||
|
|
||||||
class KeyParseError(Exception):
|
class KeyParseError(Exception):
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import importlib
|
|||||||
import collections
|
import collections
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import PYQT_VERSION_STR, QLibraryInfo
|
from PyQt5.QtCore import PYQT_VERSION_STR, QLibraryInfo
|
||||||
from PyQt5.QtNetwork import QSslSocket
|
from PyQt5.QtNetwork import QSslSocket
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
@ -49,8 +50,15 @@ from qutebrowser.misc import objects, earlyinit, sql
|
|||||||
from qutebrowser.browser import pdfjs
|
from qutebrowser.browser import pdfjs
|
||||||
|
|
||||||
|
|
||||||
DistributionInfo = collections.namedtuple(
|
@attr.s
|
||||||
'DistributionInfo', ['id', 'parsed', 'version', 'pretty'])
|
class DistributionInfo:
|
||||||
|
|
||||||
|
"""Information about the running distribution."""
|
||||||
|
|
||||||
|
id = attr.ib()
|
||||||
|
parsed = attr.ib()
|
||||||
|
version = attr.ib()
|
||||||
|
pretty = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
Distribution = usertypes.enum(
|
Distribution = usertypes.enum(
|
||||||
@ -190,24 +198,25 @@ def _module_versions():
|
|||||||
('pygments', ['__version__']),
|
('pygments', ['__version__']),
|
||||||
('yaml', ['__version__']),
|
('yaml', ['__version__']),
|
||||||
('cssutils', ['__version__']),
|
('cssutils', ['__version__']),
|
||||||
|
('attr', ['__version__']),
|
||||||
('PyQt5.QtWebEngineWidgets', []),
|
('PyQt5.QtWebEngineWidgets', []),
|
||||||
('PyQt5.QtWebKitWidgets', []),
|
('PyQt5.QtWebKitWidgets', []),
|
||||||
])
|
])
|
||||||
for name, attributes in modules.items():
|
for modname, attributes in modules.items():
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(name)
|
module = importlib.import_module(modname)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
text = '{}: no'.format(name)
|
text = '{}: no'.format(modname)
|
||||||
else:
|
else:
|
||||||
for attr in attributes:
|
for name in attributes:
|
||||||
try:
|
try:
|
||||||
text = '{}: {}'.format(name, getattr(module, attr))
|
text = '{}: {}'.format(modname, getattr(module, name))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
text = '{}: yes'.format(name)
|
text = '{}: yes'.format(modname)
|
||||||
lines.append(text)
|
lines.append(text)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
|
attrs==17.2.0
|
||||||
colorama==0.3.9
|
colorama==0.3.9
|
||||||
cssutils==1.0.2
|
cssutils==1.0.2
|
||||||
Jinja2==2.9.6
|
Jinja2==2.9.6
|
||||||
|
@ -25,16 +25,26 @@ import sys
|
|||||||
import enum
|
import enum
|
||||||
import os.path
|
import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
import collections
|
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
|
||||||
os.pardir))
|
os.pardir))
|
||||||
|
|
||||||
from scripts import utils
|
from scripts import utils
|
||||||
|
|
||||||
Message = collections.namedtuple('Message', 'typ, filename, text')
|
|
||||||
|
@attr.s
|
||||||
|
class Message:
|
||||||
|
|
||||||
|
"""A message shown by coverage.py."""
|
||||||
|
|
||||||
|
typ = attr.ib()
|
||||||
|
filename = attr.ib()
|
||||||
|
text = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
MsgType = enum.Enum('MsgType', 'insufficent_coverage, perfect_file')
|
MsgType = enum.Enum('MsgType', 'insufficent_coverage, perfect_file')
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,17 +24,29 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
import collections
|
|
||||||
import os.path
|
import os.path
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
|
||||||
os.pardir))
|
os.pardir))
|
||||||
|
|
||||||
from scripts import utils
|
from scripts import utils
|
||||||
|
|
||||||
|
|
||||||
Line = collections.namedtuple('Line', 'time, pid, uid, gid, sig, present, exe')
|
@attr.s
|
||||||
|
class Line:
|
||||||
|
|
||||||
|
"""A line in "coredumpctl list"."""
|
||||||
|
|
||||||
|
time = attr.ib()
|
||||||
|
pid = attr.ib()
|
||||||
|
uid = attr.ib()
|
||||||
|
gid = attr.ib()
|
||||||
|
sig = attr.ib()
|
||||||
|
present = attr.ib()
|
||||||
|
exe = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def _convert_present(data):
|
def _convert_present(data):
|
||||||
|
@ -115,6 +115,12 @@ def whitelist_generator():
|
|||||||
'_get_default_metavar_for_positional', '_metavar_formatter']:
|
'_get_default_metavar_for_positional', '_metavar_formatter']:
|
||||||
yield 'scripts.dev.src2asciidoc.UsageFormatter.' + attr
|
yield 'scripts.dev.src2asciidoc.UsageFormatter.' + attr
|
||||||
|
|
||||||
|
# attrs
|
||||||
|
yield 'qutebrowser.browser.webkit.network.networkmanager.ProxyId.hostname'
|
||||||
|
yield 'qutebrowser.command.command.ArgInfo._validate_exclusive'
|
||||||
|
yield 'scripts.get_coredumpctl_traces.Line.uid'
|
||||||
|
yield 'scripts.get_coredumpctl_traces.Line.gid'
|
||||||
|
|
||||||
|
|
||||||
def filter_func(item):
|
def filter_func(item):
|
||||||
"""Check if a missing function should be filtered or not.
|
"""Check if a missing function should be filtered or not.
|
||||||
|
2
setup.py
2
setup.py
@ -44,7 +44,7 @@ try:
|
|||||||
['qutebrowser = qutebrowser.qutebrowser:main']},
|
['qutebrowser = qutebrowser.qutebrowser:main']},
|
||||||
test_suite='qutebrowser.test',
|
test_suite='qutebrowser.test',
|
||||||
zip_safe=True,
|
zip_safe=True,
|
||||||
install_requires=['pypeg2', 'jinja2', 'pygments', 'PyYAML'],
|
install_requires=['pypeg2', 'jinja2', 'pygments', 'PyYAML', 'attrs'],
|
||||||
**common.setupdata
|
**common.setupdata
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
|
@ -23,6 +23,7 @@ import re
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
import pytestqt.plugin
|
import pytestqt.plugin
|
||||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QProcess, QObject,
|
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QProcess, QObject,
|
||||||
@ -58,6 +59,7 @@ class BlacklistedMessageError(Exception):
|
|||||||
"""Raised when ensure_not_logged found a message."""
|
"""Raised when ensure_not_logged found a message."""
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class Line:
|
class Line:
|
||||||
|
|
||||||
"""Container for a line of data the process emits.
|
"""Container for a line of data the process emits.
|
||||||
@ -67,12 +69,8 @@ class Line:
|
|||||||
waited_for: If Process.wait_for was used on this line already.
|
waited_for: If Process.wait_for was used on this line already.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data):
|
data = attr.ib()
|
||||||
self.data = data
|
waited_for = attr.ib(False)
|
||||||
self.waited_for = False
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '{}({!r})'.format(self.__class__.__name__, self.data)
|
|
||||||
|
|
||||||
|
|
||||||
def _render_log(data, threshold=100):
|
def _render_log(data, threshold=100):
|
||||||
|
@ -25,6 +25,7 @@ import json
|
|||||||
import os.path
|
import os.path
|
||||||
import http.client
|
import http.client
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import pyqtSignal, QUrl
|
from PyQt5.QtCore import pyqtSignal, QUrl
|
||||||
|
|
||||||
@ -99,13 +100,13 @@ class Request(testprocess.Line):
|
|||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True, cmp=False, hash=True)
|
||||||
class ExpectedRequest:
|
class ExpectedRequest:
|
||||||
|
|
||||||
"""Class to compare expected requests easily."""
|
"""Class to compare expected requests easily."""
|
||||||
|
|
||||||
def __init__(self, verb, path):
|
verb = attr.ib()
|
||||||
self.verb = verb
|
path = attr.ib()
|
||||||
self.path = path
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_request(cls, request):
|
def from_request(cls, request):
|
||||||
@ -118,13 +119,6 @@ class ExpectedRequest:
|
|||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(('ExpectedRequest', self.verb, self.path))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return ('ExpectedRequest(verb={!r}, path={!r})'
|
|
||||||
.format(self.verb, self.path))
|
|
||||||
|
|
||||||
|
|
||||||
class WebserverProcess(testprocess.Process):
|
class WebserverProcess(testprocess.Process):
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
"""Test the built-in directory browser."""
|
"""Test the built-in directory browser."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
import bs4
|
import bs4
|
||||||
|
|
||||||
@ -101,8 +101,21 @@ class DirLayout:
|
|||||||
return os.path.normpath(str(self.base))
|
return os.path.normpath(str(self.base))
|
||||||
|
|
||||||
|
|
||||||
Parsed = collections.namedtuple('Parsed', 'path, parent, folders, files')
|
@attr.s
|
||||||
Item = collections.namedtuple('Item', 'path, link, text')
|
class Parsed:
|
||||||
|
|
||||||
|
path = attr.ib()
|
||||||
|
parent = attr.ib()
|
||||||
|
folders = attr.ib()
|
||||||
|
files = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class Item:
|
||||||
|
|
||||||
|
path = attr.ib()
|
||||||
|
link = attr.ib()
|
||||||
|
text = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def parse(quteproc):
|
def parse(quteproc):
|
||||||
@ -182,6 +195,7 @@ def test_enter_folder_smoke(dir_layout, quteproc):
|
|||||||
|
|
||||||
@pytest.mark.parametrize('folder', DirLayout.layout_folders())
|
@pytest.mark.parametrize('folder', DirLayout.layout_folders())
|
||||||
def test_enter_folder(dir_layout, quteproc, folder):
|
def test_enter_folder(dir_layout, quteproc, folder):
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
quteproc.open_url(dir_layout.file_url())
|
quteproc.open_url(dir_layout.file_url())
|
||||||
quteproc.click_element_by_text(text=folder)
|
quteproc.click_element_by_text(text=folder)
|
||||||
expected_url = urlutils.file_url(dir_layout.path(folder))
|
expected_url = urlutils.file_url(dir_layout.path(folder))
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import textwrap
|
import textwrap
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
import yaml
|
import yaml
|
||||||
import pytest
|
import pytest
|
||||||
import bs4
|
import bs4
|
||||||
@ -36,8 +36,11 @@ def collect_tests():
|
|||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
||||||
ParsedFile = collections.namedtuple('ParsedFile', ['target',
|
@attr.s
|
||||||
'qtwebengine_todo'])
|
class ParsedFile:
|
||||||
|
|
||||||
|
target = attr.ib()
|
||||||
|
qtwebengine_todo = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class InvalidFile(Exception):
|
class InvalidFile(Exception):
|
||||||
|
@ -26,13 +26,13 @@ See https://pytest.org/latest/fixture.html
|
|||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import collections
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import itertools
|
import itertools
|
||||||
import textwrap
|
import textwrap
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
import py.path # pylint: disable=no-name-in-module
|
import py.path # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
@ -53,7 +53,12 @@ class WinRegistryHelper:
|
|||||||
|
|
||||||
"""Helper class for win_registry."""
|
"""Helper class for win_registry."""
|
||||||
|
|
||||||
FakeWindow = collections.namedtuple('FakeWindow', ['registry'])
|
@attr.s
|
||||||
|
class FakeWindow:
|
||||||
|
|
||||||
|
"""A fake window object for the registry."""
|
||||||
|
|
||||||
|
registry = attr.ib()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._ids = []
|
self._ids = []
|
||||||
@ -161,8 +166,12 @@ def fake_web_tab(stubs, tab_registry, mode_manager, qapp):
|
|||||||
|
|
||||||
def _generate_cmdline_tests():
|
def _generate_cmdline_tests():
|
||||||
"""Generate testcases for test_split_binding."""
|
"""Generate testcases for test_split_binding."""
|
||||||
# pylint: disable=invalid-name
|
@attr.s
|
||||||
TestCase = collections.namedtuple('TestCase', 'cmd, valid')
|
class TestCase:
|
||||||
|
|
||||||
|
cmd = attr.ib()
|
||||||
|
valid = attr.ib()
|
||||||
|
|
||||||
separators = [';;', ' ;; ', ';; ', ' ;;']
|
separators = [';;', ' ;; ', ';; ', ' ;;']
|
||||||
invalid = ['foo', '']
|
invalid = ['foo', '']
|
||||||
valid = ['leave-mode', 'hint all']
|
valid = ['leave-mode', 'hint all']
|
||||||
|
@ -20,14 +20,20 @@
|
|||||||
"""pytest helper to monkeypatch the message module."""
|
"""pytest helper to monkeypatch the message module."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.utils import usertypes, message
|
from qutebrowser.utils import usertypes, message
|
||||||
|
|
||||||
|
|
||||||
Message = collections.namedtuple('Message', ['level', 'text'])
|
@attr.s
|
||||||
|
class Message:
|
||||||
|
|
||||||
|
"""Information about a shown message."""
|
||||||
|
|
||||||
|
level = attr.ib()
|
||||||
|
text = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class MessageMock:
|
class MessageMock:
|
||||||
@ -35,8 +41,8 @@ class MessageMock:
|
|||||||
"""Helper object for message_mock.
|
"""Helper object for message_mock.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
Message: A namedtuple representing a message.
|
Message: A object representing a message.
|
||||||
messages: A list of Message tuples.
|
messages: A list of Message objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
|
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
|
||||||
from PyQt5.QtNetwork import (QNetworkRequest, QAbstractNetworkCache,
|
from PyQt5.QtNetwork import (QNetworkRequest, QAbstractNetworkCache,
|
||||||
QNetworkCacheMetaData)
|
QNetworkCacheMetaData)
|
||||||
@ -202,9 +203,9 @@ class FakeNetworkReply:
|
|||||||
def fake_qprocess():
|
def fake_qprocess():
|
||||||
"""Factory for a QProcess mock which has the QProcess enum values."""
|
"""Factory for a QProcess mock which has the QProcess enum values."""
|
||||||
m = mock.Mock(spec=QProcess)
|
m = mock.Mock(spec=QProcess)
|
||||||
for attr in ['NormalExit', 'CrashExit', 'FailedToStart', 'Crashed',
|
for name in ['NormalExit', 'CrashExit', 'FailedToStart', 'Crashed',
|
||||||
'Timedout', 'WriteError', 'ReadError', 'UnknownError']:
|
'Timedout', 'WriteError', 'ReadError', 'UnknownError']:
|
||||||
setattr(m, attr, getattr(QProcess, attr))
|
setattr(m, name, getattr(QProcess, name))
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
@ -315,27 +316,26 @@ class FakeSignal:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class FakeCmdUtils:
|
class FakeCmdUtils:
|
||||||
|
|
||||||
"""Stub for cmdutils which provides a cmd_dict."""
|
"""Stub for cmdutils which provides a cmd_dict."""
|
||||||
|
|
||||||
def __init__(self, commands):
|
cmd_dict = attr.ib()
|
||||||
self.cmd_dict = commands
|
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True)
|
||||||
class FakeCommand:
|
class FakeCommand:
|
||||||
|
|
||||||
"""A simple command stub which has a description."""
|
"""A simple command stub which has a description."""
|
||||||
|
|
||||||
def __init__(self, name='', desc='', hide=False, debug=False,
|
name = attr.ib('')
|
||||||
deprecated=False, completion=None, maxsplit=None):
|
desc = attr.ib('')
|
||||||
self.desc = desc
|
hide = attr.ib(False)
|
||||||
self.name = name
|
debug = attr.ib(False)
|
||||||
self.hide = hide
|
deprecated = attr.ib(False)
|
||||||
self.debug = debug
|
completion = attr.ib(None)
|
||||||
self.deprecated = deprecated
|
maxsplit = attr.ib(None)
|
||||||
self.completion = completion
|
|
||||||
self.maxsplit = maxsplit
|
|
||||||
|
|
||||||
|
|
||||||
class FakeTimer(QObject):
|
class FakeTimer(QObject):
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
"""Tests for browser.signalfilter."""
|
"""Tests for browser.signalfilter."""
|
||||||
|
|
||||||
import collections
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
|
||||||
|
|
||||||
@ -47,7 +47,11 @@ class Signaller(QObject):
|
|||||||
self.filtered_signal_arg = s
|
self.filtered_signal_arg = s
|
||||||
|
|
||||||
|
|
||||||
Objects = collections.namedtuple('Objects', 'signal_filter, signaller')
|
@attr.s
|
||||||
|
class Objects:
|
||||||
|
|
||||||
|
signal_filter = attr.ib()
|
||||||
|
signaller = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
import bs4
|
import bs4
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
@ -109,8 +109,18 @@ def _file_url(path):
|
|||||||
|
|
||||||
class TestDirbrowserHtml:
|
class TestDirbrowserHtml:
|
||||||
|
|
||||||
Parsed = collections.namedtuple('Parsed', 'parent, folders, files')
|
@attr.s
|
||||||
Item = collections.namedtuple('Item', 'link, text')
|
class Parsed:
|
||||||
|
|
||||||
|
parent = attr.ib()
|
||||||
|
folders = attr.ib()
|
||||||
|
files = attr.ib()
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class Item:
|
||||||
|
|
||||||
|
link = attr.ib()
|
||||||
|
text = attr.ib()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def parser(self):
|
def parser(self):
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
|
|
||||||
"""Tests for webelement.tabhistory."""
|
"""Tests for webelement.tabhistory."""
|
||||||
|
|
||||||
import collections
|
import attr
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, QPoint
|
from PyQt5.QtCore import QUrl, QPoint
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -50,7 +49,11 @@ ITEMS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
Objects = collections.namedtuple('Objects', 'history, user_data')
|
@attr.s
|
||||||
|
class Objects:
|
||||||
|
|
||||||
|
history = attr.ib()
|
||||||
|
user_data = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -24,6 +24,7 @@ import collections.abc
|
|||||||
import operator
|
import operator
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import QRect, QPoint, QUrl
|
from PyQt5.QtCore import QRect, QPoint, QUrl
|
||||||
QWebElement = pytest.importorskip('PyQt5.QtWebKit').QWebElement
|
QWebElement = pytest.importorskip('PyQt5.QtWebKit').QWebElement
|
||||||
@ -525,7 +526,12 @@ class TestIsVisibleIframe:
|
|||||||
elem1-elem4: FakeWebElements to test.
|
elem1-elem4: FakeWebElements to test.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Objects = collections.namedtuple('Objects', ['frame', 'iframe', 'elems'])
|
@attr.s
|
||||||
|
class Objects:
|
||||||
|
|
||||||
|
frame = attr.ib()
|
||||||
|
iframe = attr.ib()
|
||||||
|
elems = attr.ib()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def objects(self, stubs):
|
def objects(self, stubs):
|
||||||
@ -550,7 +556,7 @@ class TestIsVisibleIframe:
|
|||||||
##############################
|
##############################
|
||||||
300, 0 300, 300
|
300, 0 300, 300
|
||||||
|
|
||||||
Returns an Objects namedtuple with frame/iframe/elems attributes.
|
Returns an Objects object with frame/iframe/elems attributes.
|
||||||
"""
|
"""
|
||||||
frame = stubs.FakeWebFrame(QRect(0, 0, 300, 300))
|
frame = stubs.FakeWebFrame(QRect(0, 0, 300, 300))
|
||||||
iframe = stubs.FakeWebFrame(QRect(0, 10, 100, 100), parent=frame)
|
iframe = stubs.FakeWebFrame(QRect(0, 10, 100, 100), parent=frame)
|
||||||
@ -621,7 +627,7 @@ class TestIsVisibleIframe:
|
|||||||
##############################
|
##############################
|
||||||
300, 0 300, 300
|
300, 0 300, 300
|
||||||
|
|
||||||
Returns an Objects namedtuple with frame/iframe/elems attributes.
|
Returns an Objects object with frame/iframe/elems attributes.
|
||||||
"""
|
"""
|
||||||
frame = stubs.FakeWebFrame(QRect(0, 0, 300, 300))
|
frame = stubs.FakeWebFrame(QRect(0, 0, 300, 300))
|
||||||
iframe = stubs.FakeWebFrame(QRect(0, 10, 100, 100), parent=frame)
|
iframe = stubs.FakeWebFrame(QRect(0, 10, 100, 100), parent=frame)
|
||||||
|
@ -21,12 +21,12 @@
|
|||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import collections
|
|
||||||
import itertools
|
import itertools
|
||||||
import warnings
|
import warnings
|
||||||
import inspect
|
import inspect
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
import hypothesis
|
import hypothesis
|
||||||
from hypothesis import strategies
|
from hypothesis import strategies
|
||||||
@ -54,15 +54,14 @@ class Font(QFont):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def fromdesc(cls, desc):
|
def fromdesc(cls, desc):
|
||||||
"""Get a Font based on a font description."""
|
"""Get a Font based on a font description."""
|
||||||
style, weight, ptsize, pxsize, family = desc
|
|
||||||
f = cls()
|
f = cls()
|
||||||
f.setStyle(style)
|
f.setStyle(desc.style)
|
||||||
f.setWeight(weight)
|
f.setWeight(desc.weight)
|
||||||
if ptsize is not None and ptsize != -1:
|
if desc.pt is not None and desc.pt != -1:
|
||||||
f.setPointSize(ptsize)
|
f.setPointSize(desc.pt)
|
||||||
if pxsize is not None and ptsize != -1:
|
if desc.px is not None and desc.pt != -1:
|
||||||
f.setPixelSize(pxsize)
|
f.setPixelSize(desc.px)
|
||||||
f.setFamily(family)
|
f.setFamily(desc.family)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
@ -1195,8 +1194,14 @@ class TestColors:
|
|||||||
klass().to_py(val)
|
klass().to_py(val)
|
||||||
|
|
||||||
|
|
||||||
FontDesc = collections.namedtuple('FontDesc',
|
@attr.s
|
||||||
['style', 'weight', 'pt', 'px', 'family'])
|
class FontDesc:
|
||||||
|
|
||||||
|
style = attr.ib()
|
||||||
|
weight = attr.ib()
|
||||||
|
pt = attr.ib()
|
||||||
|
px = attr.ib()
|
||||||
|
family = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class TestFont:
|
class TestFont:
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import getpass
|
import getpass
|
||||||
import collections
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import pyqtSignal, QObject
|
from PyQt5.QtCore import pyqtSignal, QObject
|
||||||
from PyQt5.QtNetwork import QLocalServer, QLocalSocket, QAbstractSocket
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket, QAbstractSocket
|
||||||
@ -597,8 +597,13 @@ def test_ipcserver_socket_none_error(ipc_server, caplog):
|
|||||||
|
|
||||||
class TestSendOrListen:
|
class TestSendOrListen:
|
||||||
|
|
||||||
Args = collections.namedtuple('Args', 'no_err_windows, basedir, command, '
|
@attr.s
|
||||||
'target')
|
class Args:
|
||||||
|
|
||||||
|
no_err_windows = attr.ib()
|
||||||
|
basedir = attr.ib()
|
||||||
|
command = attr.ib()
|
||||||
|
target = attr.ib()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def args(self):
|
def args(self):
|
||||||
@ -623,10 +628,10 @@ class TestSendOrListen:
|
|||||||
def qlocalsocket_mock(self, mocker):
|
def qlocalsocket_mock(self, mocker):
|
||||||
m = mocker.patch('qutebrowser.misc.ipc.QLocalSocket', autospec=True)
|
m = mocker.patch('qutebrowser.misc.ipc.QLocalSocket', autospec=True)
|
||||||
m().errorString.return_value = "Error string"
|
m().errorString.return_value = "Error string"
|
||||||
for attr in ['UnknownSocketError', 'UnconnectedState',
|
for name in ['UnknownSocketError', 'UnconnectedState',
|
||||||
'ConnectionRefusedError', 'ServerNotFoundError',
|
'ConnectionRefusedError', 'ServerNotFoundError',
|
||||||
'PeerClosedError']:
|
'PeerClosedError']:
|
||||||
setattr(m, attr, getattr(QLocalSocket, attr))
|
setattr(m, name, getattr(QLocalSocket, name))
|
||||||
return m
|
return m
|
||||||
|
|
||||||
@pytest.mark.linux(reason="Flaky on Windows and macOS")
|
@pytest.mark.linux(reason="Flaky on Windows and macOS")
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""Tests for qutebrowser.misc.split."""
|
"""Tests for qutebrowser.misc.split."""
|
||||||
import collections
|
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.misc import split
|
from qutebrowser.misc import split
|
||||||
@ -104,21 +104,26 @@ foo\ bar/foo bar/foo\ bar/
|
|||||||
|
|
||||||
|
|
||||||
def _parse_split_test_data_str():
|
def _parse_split_test_data_str():
|
||||||
"""Parse the test data set into a namedtuple to use in tests.
|
"""Parse the test data set into a TestCase object to use in tests.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of namedtuples with str attributes: input, keep, no_keep
|
A list of TestCase objects with str attributes: inp, keep, no_keep
|
||||||
"""
|
"""
|
||||||
tuple_class = collections.namedtuple('TestCase', 'input, keep, no_keep')
|
@attr.s
|
||||||
|
class TestCase:
|
||||||
|
|
||||||
|
inp = attr.ib()
|
||||||
|
keep = attr.ib()
|
||||||
|
no_keep = attr.ib()
|
||||||
|
|
||||||
for line in test_data_str.splitlines():
|
for line in test_data_str.splitlines():
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
data = line.split('/')
|
data = line.split('/')
|
||||||
item = tuple_class(input=data[0], keep=data[1].split('|'),
|
item = TestCase(inp=data[0], keep=data[1].split('|'),
|
||||||
no_keep=data[2].split('|'))
|
no_keep=data[2].split('|'))
|
||||||
yield item
|
yield item
|
||||||
yield tuple_class(input='', keep=[], no_keep=[])
|
yield TestCase(inp='', keep=[], no_keep=[])
|
||||||
|
|
||||||
|
|
||||||
class TestSplit:
|
class TestSplit:
|
||||||
@ -137,17 +142,17 @@ class TestSplit:
|
|||||||
|
|
||||||
def test_split(self, split_test_case):
|
def test_split(self, split_test_case):
|
||||||
"""Test splitting."""
|
"""Test splitting."""
|
||||||
items = split.split(split_test_case.input)
|
items = split.split(split_test_case.inp)
|
||||||
assert items == split_test_case.keep
|
assert items == split_test_case.keep
|
||||||
|
|
||||||
def test_split_keep_original(self, split_test_case):
|
def test_split_keep_original(self, split_test_case):
|
||||||
"""Test if splitting with keep=True yields the original string."""
|
"""Test if splitting with keep=True yields the original string."""
|
||||||
items = split.split(split_test_case.input, keep=True)
|
items = split.split(split_test_case.inp, keep=True)
|
||||||
assert ''.join(items) == split_test_case.input
|
assert ''.join(items) == split_test_case.inp
|
||||||
|
|
||||||
def test_split_keep(self, split_test_case):
|
def test_split_keep(self, split_test_case):
|
||||||
"""Test splitting with keep=True."""
|
"""Test splitting with keep=True."""
|
||||||
items = split.split(split_test_case.input, keep=True)
|
items = split.split(split_test_case.inp, keep=True)
|
||||||
assert items == split_test_case.no_keep
|
assert items == split_test_case.no_keep
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ import json
|
|||||||
import os.path
|
import os.path
|
||||||
import types
|
import types
|
||||||
import textwrap
|
import textwrap
|
||||||
import collections
|
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import QStandardPaths
|
from PyQt5.QtCore import QStandardPaths
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -207,9 +207,6 @@ class TestStandardDir:
|
|||||||
assert func().split(os.sep)[-elems:] == expected
|
assert func().split(os.sep)[-elems:] == expected
|
||||||
|
|
||||||
|
|
||||||
DirArgTest = collections.namedtuple('DirArgTest', 'arg, expected')
|
|
||||||
|
|
||||||
|
|
||||||
class TestArguments:
|
class TestArguments:
|
||||||
|
|
||||||
"""Tests the --basedir argument."""
|
"""Tests the --basedir argument."""
|
||||||
@ -370,10 +367,16 @@ class TestMoveWindowsAndMacOS:
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def files(self, tmpdir):
|
def files(self, tmpdir):
|
||||||
files = collections.namedtuple('Files', [
|
|
||||||
'auto_config_dir', 'config_dir',
|
@attr.s
|
||||||
'local_data_dir', 'roaming_data_dir'])
|
class Files:
|
||||||
return files(
|
|
||||||
|
auto_config_dir = attr.ib()
|
||||||
|
config_dir = attr.ib()
|
||||||
|
local_data_dir = attr.ib()
|
||||||
|
roaming_data_dir = attr.ib()
|
||||||
|
|
||||||
|
return Files(
|
||||||
auto_config_dir=tmpdir / 'auto_config' / APPNAME,
|
auto_config_dir=tmpdir / 'auto_config' / APPNAME,
|
||||||
config_dir=tmpdir / 'config' / APPNAME,
|
config_dir=tmpdir / 'config' / APPNAME,
|
||||||
local_data_dir=tmpdir / 'data' / APPNAME,
|
local_data_dir=tmpdir / 'data' / APPNAME,
|
||||||
@ -412,11 +415,17 @@ class TestMove:
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def dirs(self, tmpdir):
|
def dirs(self, tmpdir):
|
||||||
dirs = collections.namedtuple('Dirs', ['old', 'new',
|
@attr.s
|
||||||
'old_file', 'new_file'])
|
class Dirs:
|
||||||
|
|
||||||
|
old = attr.ib()
|
||||||
|
new = attr.ib()
|
||||||
|
old_file = attr.ib()
|
||||||
|
new_file = attr.ib()
|
||||||
|
|
||||||
old_dir = tmpdir / 'old'
|
old_dir = tmpdir / 'old'
|
||||||
new_dir = tmpdir / 'new'
|
new_dir = tmpdir / 'new'
|
||||||
return dirs(old=old_dir, new=new_dir,
|
return Dirs(old=old_dir, new=new_dir,
|
||||||
old_file=old_dir / 'file', new_file=new_dir / 'file')
|
old_file=old_dir / 'file', new_file=new_dir / 'file')
|
||||||
|
|
||||||
def test_no_old_dir(self, dirs, caplog):
|
def test_no_old_dir(self, dirs, caplog):
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
"""Tests for qutebrowser.utils.urlutils."""
|
"""Tests for qutebrowser.utils.urlutils."""
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import collections
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
from PyQt5.QtNetwork import QNetworkProxy
|
from PyQt5.QtNetwork import QNetworkProxy
|
||||||
import pytest
|
import pytest
|
||||||
@ -38,7 +38,7 @@ class FakeDNS:
|
|||||||
"""Helper class for the fake_dns fixture.
|
"""Helper class for the fake_dns fixture.
|
||||||
|
|
||||||
Class attributes:
|
Class attributes:
|
||||||
FakeDNSAnswer: Helper class/namedtuple imitating a QHostInfo object
|
FakeDNSAnswer: Helper class imitating a QHostInfo object
|
||||||
(used by fromname_mock).
|
(used by fromname_mock).
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@ -48,7 +48,10 @@ class FakeDNS:
|
|||||||
when fromname_mock is called.
|
when fromname_mock is called.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
FakeDNSAnswer = collections.namedtuple('FakeDNSAnswer', ['error'])
|
@attr.s
|
||||||
|
class FakeDNSAnswer:
|
||||||
|
|
||||||
|
error = attr.ib()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.used = False
|
self.used = False
|
||||||
|
@ -25,11 +25,11 @@ import os.path
|
|||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import functools
|
import functools
|
||||||
import collections
|
|
||||||
import socket
|
import socket
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
|
import attr
|
||||||
from PyQt5.QtCore import Qt, QUrl
|
from PyQt5.QtCore import Qt, QUrl
|
||||||
from PyQt5.QtGui import QColor, QClipboard
|
from PyQt5.QtGui import QColor, QClipboard
|
||||||
import pytest
|
import pytest
|
||||||
@ -157,7 +157,11 @@ class TestInterpolateColor:
|
|||||||
white: The Color black as a valid Color for tests.
|
white: The Color black as a valid Color for tests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Colors = collections.namedtuple('Colors', ['white', 'black'])
|
@attr.s
|
||||||
|
class Colors:
|
||||||
|
|
||||||
|
white = attr.ib()
|
||||||
|
black = attr.ib()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def colors(self):
|
def colors(self):
|
||||||
|
@ -32,6 +32,7 @@ import logging
|
|||||||
import textwrap
|
import textwrap
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
import attr
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import qutebrowser
|
import qutebrowser
|
||||||
@ -474,8 +475,8 @@ def test_path_info(monkeypatch, equal):
|
|||||||
'runtime': lambda: 'RUNTIME PATH',
|
'runtime': lambda: 'RUNTIME PATH',
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr, val in patches.items():
|
for name, val in patches.items():
|
||||||
monkeypatch.setattr(version.standarddir, attr, val)
|
monkeypatch.setattr(version.standarddir, name, val)
|
||||||
|
|
||||||
pathinfo = version._path_info()
|
pathinfo = version._path_info()
|
||||||
|
|
||||||
@ -515,6 +516,7 @@ class ImportFake:
|
|||||||
('pygments', True),
|
('pygments', True),
|
||||||
('yaml', True),
|
('yaml', True),
|
||||||
('cssutils', True),
|
('cssutils', True),
|
||||||
|
('attr', True),
|
||||||
('PyQt5.QtWebEngineWidgets', True),
|
('PyQt5.QtWebEngineWidgets', True),
|
||||||
('PyQt5.QtWebKitWidgets', True),
|
('PyQt5.QtWebKitWidgets', True),
|
||||||
])
|
])
|
||||||
@ -630,6 +632,7 @@ class TestModuleVersions:
|
|||||||
('pygments', True),
|
('pygments', True),
|
||||||
('yaml', True),
|
('yaml', True),
|
||||||
('cssutils', True),
|
('cssutils', True),
|
||||||
|
('attr', True),
|
||||||
])
|
])
|
||||||
def test_existing_attributes(self, name, has_version):
|
def test_existing_attributes(self, name, has_version):
|
||||||
"""Check if all dependencies have an expected __version__ attribute.
|
"""Check if all dependencies have an expected __version__ attribute.
|
||||||
@ -817,17 +820,16 @@ def test_chromium_version_unpatched(qapp):
|
|||||||
assert version._chromium_version() not in ['', 'unknown', 'unavailable']
|
assert version._chromium_version() not in ['', 'unknown', 'unavailable']
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
class VersionParams:
|
class VersionParams:
|
||||||
|
|
||||||
def __init__(self, name, git_commit=True, frozen=False, style=True,
|
name = attr.ib()
|
||||||
with_webkit=True, known_distribution=True, ssl_support=True):
|
git_commit = attr.ib(True)
|
||||||
self.name = name
|
frozen = attr.ib(False)
|
||||||
self.git_commit = git_commit
|
style = attr.ib(True)
|
||||||
self.frozen = frozen
|
with_webkit = attr.ib(True)
|
||||||
self.style = style
|
known_distribution = attr.ib(True)
|
||||||
self.with_webkit = with_webkit
|
ssl_support = attr.ib(True)
|
||||||
self.known_distribution = known_distribution
|
|
||||||
self.ssl_support = ssl_support
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('params', [
|
@pytest.mark.parametrize('params', [
|
||||||
@ -901,8 +903,8 @@ def test_version_output(params, stubs, monkeypatch):
|
|||||||
|
|
||||||
substitutions['ssl'] = 'SSL VERSION' if params.ssl_support else 'no'
|
substitutions['ssl'] = 'SSL VERSION' if params.ssl_support else 'no'
|
||||||
|
|
||||||
for attr, val in patches.items():
|
for name, val in patches.items():
|
||||||
monkeypatch.setattr('qutebrowser.utils.version.' + attr, val)
|
monkeypatch.setattr('qutebrowser.utils.version.' + name, val)
|
||||||
|
|
||||||
if params.frozen:
|
if params.frozen:
|
||||||
monkeypatch.setattr(sys, 'frozen', True, raising=False)
|
monkeypatch.setattr(sys, 'frozen', True, raising=False)
|
||||||
|
Loading…
Reference in New Issue
Block a user