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