Merge branch 'master' into mark-del-no-args

This commit is contained in:
Marshall Lochbaum 2016-07-23 12:12:19 -04:00
commit 3ffb726b98
56 changed files with 295 additions and 128 deletions

View File

@ -1,5 +1,5 @@
[flake8]
exclude = .venv,.hypothesis,.git,__pycache__,resources.py
exclude = .*,__pycache__,resources.py
# E128: continuation line under-indented for visual indent
# E226: missing whitespace around arithmetic operator
# E265: Block comment should start with '#'

View File

@ -9,6 +9,7 @@ load-plugins=qute_pylint.config,
qute_pylint.settrace,
pylint.extensions.bad_builtin,
pylint.extensions.docstyle
persistent=n
[MESSAGES CONTROL]
enable=all

View File

@ -31,6 +31,7 @@ Added
- New `:repeat-command` command (mapped to `.`) to repeat the last command.
Note that two former default bundings conflict with that binding, unbinding
them via `:unbind .i` and `:unbind .o` is recommended.
- New `qute:bookmarks` page which displays all bookmarks and quickmarks.
Changed
~~~~~~~
@ -47,6 +48,7 @@ Changed
Fixed
-----
- Copmpatibility with PyQt 5.7
- Fixed some configuration values being lost when a config option gets removed
from qutebrowser's code.

View File

@ -181,6 +181,7 @@ Contributors, sorted by the number of commits in descending order:
* Tomasz Kramkowski
* Ismail S
* Halfwit
* David Vogt
* rikn00
* kanikaa1234
* haitaka
@ -210,6 +211,7 @@ Contributors, sorted by the number of commits in descending order:
* Regina Hug
* Mathias Fussenegger
* Marcelo Santos
* Jean-Louis Fuchs
* Fritz V155 Reichwald
* Franz Fellner
* zwarag

View File

@ -120,7 +120,7 @@ Syntax: +:bookmark-add ['url'] ['title']+
Save the current page as a bookmark, or a specific url.
If no url and title are provided, then save the current page as a bookmark. If a url and title have been provided, then save the given url as a bookmark with the provided title.
If no url and title are provided, then save the current page as a bookmark. If a url and title have been provided, then save the given url as a bookmark with the provided title. You can view all saved bookmarks on the link:qute://bookmarks[bookmarks page].
==== positional arguments
* +'url'+: url to save as a bookmark. If None, use url of current page.
@ -512,6 +512,8 @@ Syntax: +:quickmark-add 'url' 'name'+
Add a new quickmark.
You can view all saved quickmarks on the link:qute://bookmarks[bookmarks page].
==== positional arguments
* +'url'+: The url to add as quickmark.
* +'name'+: The name for the new quickmark.

View File

@ -50,7 +50,7 @@ $ git checkout symbols
$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
$ cd qt5
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
$ cd ../pyqt5
$ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug
----
@ -76,7 +76,7 @@ Server = http://qutebrowser.org/qt-debug/$arch
Then install the packages:
----
# pacman -Suy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug
# pacman -Suy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug,qt5-webengine-debug
----
The `-debug` packages conflict with the non-debug variants - it's safe to

View File

@ -4,7 +4,7 @@ astroid==1.4.7
isort==4.2.5
lazy-object-proxy==1.2.2
mccabe==0.5.0
pylint==1.6.1
pylint==1.6.4
./scripts/dev/pylint_checkers
requests==2.10.0
six==1.10.0

View File

@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
beautifulsoup4==4.4.1
CherryPy==6.1.0
beautifulsoup4==4.5.0
CherryPy==6.2.0
coverage==4.1
decorator==4.0.10
Flask==0.10.1 # rq.filter: < 0.11.0
@ -28,5 +28,5 @@ pytest-rerunfailures==2.0.0
pytest-travis-fold==1.2.0
pytest-xvfb==0.2.0
six==1.10.0
vulture==0.9
vulture==0.10
Werkzeug==0.11.10

View File

@ -1,3 +1,3 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
vulture==0.9
vulture==0.10

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
#

View File

@ -1,4 +1,4 @@
#!/bin/bash -e
#!/usr/bin/env bash -e
# Both standalone script and qutebrowser userscript that opens a rofi menu with
# all files from the download director and opens the selected file. It works
# both as a userscript and a standalone script that is called from outside of

View File

@ -1,4 +1,4 @@
#!/bin/bash -e
#!/usr/bin/env bash -e
help() {
blink=$'\e[1;31m' reset=$'\e[0m'
cat <<EOF

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
#

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
#
# Behavior:
# Userscript for qutebrowser which adds a task to taskwarrior.

View File

@ -1,4 +1,4 @@
#!/bin/bash -e
#!/usr/bin/env bash -e
#
# Behavior:
# Userscript for qutebrowser which views the current web page in mpv using

View File

@ -337,7 +337,6 @@ def _save_version():
state_config['general']['version'] = qutebrowser.__version__
@pyqtSlot('QWidget*', 'QWidget*')
def on_focus_changed(_old, new):
"""Register currently focused main window in the object registry."""
if not isinstance(new, QWidget):
@ -355,7 +354,6 @@ def on_focus_changed(_old, new):
_maybe_hide_mouse_cursor()
@pyqtSlot(QUrl)
def open_desktopservices_url(url):
"""Handler to open a URL via QDesktopServices."""
win_id = mainwindow.get_window(via_ipc=True, force_window=False)
@ -476,7 +474,6 @@ class Quitter:
self._shutting_down = False
self._args = args
@pyqtSlot()
def on_last_window_closed(self):
"""Slot which gets invoked when the last window was closed."""
self.shutdown(last_window=True)

View File

@ -24,7 +24,6 @@ import itertools
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QLayout
from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
@ -122,7 +121,6 @@ class AbstractPrinting:
def to_pdf(self, filename):
raise NotImplementedError
@pyqtSlot(QPrinter)
def to_printer(self, printer):
raise NotImplementedError
@ -354,6 +352,9 @@ class AbstractScroller(QObject):
self._tab = tab
self._widget = None
def _init_widget(self, widget):
self._widget = widget
def pos_px(self):
raise NotImplementedError
@ -513,7 +514,7 @@ class AbstractTab(QWidget):
self._layout = WrapperLayout(widget, self)
self._widget = widget
self.history._history = widget.history()
self.scroll._widget = widget
self.scroll._init_widget(widget)
self.caret._widget = widget
self.zoom._widget = widget
self.search._widget = widget

View File

@ -1128,6 +1128,9 @@ class CommandDispatcher:
If a url and title have been provided, then save the given url as
a bookmark with the provided title.
You can view all saved bookmarks on the
link:qute://bookmarks[bookmarks page].
Args:
url: url to save as a bookmark. If None, use url of current page.
title: title of the new bookmark.

View File

@ -52,7 +52,6 @@ class WordHintingError(Exception):
"""Exception raised on errors during word hinting."""
@pyqtSlot(usertypes.KeyMode)
def on_mode_entered(mode, win_id):
"""Stop hinting when insert mode was entered."""
if mode == usertypes.KeyMode.insert:
@ -201,6 +200,7 @@ class HintManager(QObject):
window=self._win_id)
message_bridge.maybe_reset_text(text)
self._context = None
self._filterstr = None
def _hint_strings(self, elems):
"""Calculate the hint strings for elems.

View File

@ -179,6 +179,9 @@ class QuickmarkManager(UrlMarkManager):
def quickmark_add(self, win_id, url, name):
"""Add a new quickmark.
You can view all saved quickmarks on the
link:qute://bookmarks[bookmarks page].
Args:
win_id: The window ID to display the errors in.
url: The url to add as quickmark.

View File

@ -25,7 +25,6 @@
from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint
from PyQt5.QtGui import QKeyEvent, QIcon
from PyQt5.QtWidgets import QApplication
from PyQt5.QtPrintSupport import QPrinter
# pylint: disable=no-name-in-module,import-error,useless-suppression
from PyQt5.QtWebEngineWidgets import QWebEnginePage
# pylint: enable=no-name-in-module,import-error,useless-suppression
@ -51,7 +50,6 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
def to_pdf(self, filename):
self._widget.page().printToPdf(filename)
@pyqtSlot(QPrinter)
def to_printer(self, printer):
# Should never be called
assert False
@ -182,6 +180,21 @@ class WebEngineScroller(browsertab.AbstractScroller):
"""QtWebEngine implementations related to scrolling."""
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._pos_perc = (None, None)
self._pos_px = QPoint()
def _init_widget(self, widget):
super()._init_widget(widget)
page = widget.page()
try:
page.scrollPositionChanged.connect(
self._on_scroll_pos_changed)
except AttributeError:
log.stub('scrollPositionChanged, on Qt < 5.7')
self._on_scroll_pos_changed()
def _key_press(self, key, count=1):
# FIXME:qtwebengine Abort scrolling if the minimum/maximum was reached.
press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0)
@ -193,24 +206,27 @@ class WebEngineScroller(browsertab.AbstractScroller):
QApplication.postEvent(recipient, press_evt)
QApplication.postEvent(recipient, release_evt)
@pyqtSlot()
def _on_scroll_pos_changed(self):
"""Update the scroll position attributes when it changed."""
def update_scroll_pos(jsret):
"""Callback after getting scroll position via JS."""
assert isinstance(jsret, dict)
self._pos_perc = (jsret['perc']['x'], jsret['perc']['y'])
self._pos_px = QPoint(jsret['px']['x'], jsret['px']['y'])
self.perc_changed.emit(*self._pos_perc)
js_code = """
{scroll_js}
scroll_pos();
""".format(scroll_js=utils.read_file('javascript/scroll.js'))
self._tab.run_js_async(js_code, update_scroll_pos)
def pos_px(self):
log.stub()
return QPoint(0, 0)
return self._pos_px
def pos_perc(self):
page = self._widget.page()
try:
size = page.contentsSize()
pos = page.scrollPosition()
except AttributeError:
# Added in Qt 5.7
log.stub('on Qt < 5.7')
return (None, None)
else:
# FIXME:qtwebengine is this correct?
perc_x = 100 / size.width() * pos.x()
perc_y = 100 / size.height() * pos.y()
return (perc_x, perc_y)
return self._pos_perc
def to_perc(self, x=None, y=None):
js_code = """
@ -260,7 +276,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
self._key_press(Qt.Key_PageDown, count)
def at_top(self):
log.stub()
return self.pos_px().y() == 0
def at_bottom(self):
log.stub()
@ -409,5 +425,3 @@ class WebEngineTab(browsertab.AbstractTab):
view.iconChanged.connect(self.icon_changed)
except AttributeError:
log.stub('iconChanged, on Qt < 5.7')
# FIXME:qtwebengine stub this?
# view.scroll.pos_changed.connect(self.scroll.perc_changed)

View File

@ -261,3 +261,18 @@ def qute_pdfjs(_win_id, request):
"pdfjs resource requested but not found: {}".format(e.path))
raise QuteSchemeError("Can't find pdfjs resource '{}'".format(e.path),
QNetworkReply.ContentNotFoundError)
@add_handler('bookmarks')
def qute_bookmarks(_win_id, _request):
"""Handler for qute:bookmarks. Display all quickmarks / bookmarks."""
bookmarks = sorted(objreg.get('bookmark-manager').marks.items(),
key=lambda x: x[1]) # Sort by title
quickmarks = sorted(objreg.get('quickmark-manager').marks.items(),
key=lambda x: x[0]) # Sort by name
html = jinja.render('bookmarks.html',
title='Bookmarks',
bookmarks=bookmarks,
quickmarks=quickmarks)
return html.encode('UTF-8', errors='xmlcharrefreplace')

View File

@ -55,7 +55,6 @@ class WebKitPrinting(browsertab.AbstractPrinting):
printer.setOutputFileName(filename)
self.to_printer(printer)
@pyqtSlot(QPrinter)
def to_printer(self, printer):
self._widget.print(printer)

View File

@ -27,8 +27,6 @@ Module attributes:
import functools
from PyQt5.QtCore import pyqtSlot
from qutebrowser.completion.models import (miscmodels, urlmodel, configmodel,
base)
from qutebrowser.utils import objreg, usertypes, log, debug
@ -84,7 +82,6 @@ def _init_setting_completions():
_instances[usertypes.Completion.value][sectname][opt] = val_model
@pyqtSlot()
def init_quickmark_completions():
"""Initialize quickmark completion models."""
log.completion.debug("Initializing quickmark completion.")
@ -96,7 +93,6 @@ def init_quickmark_completions():
_instances[usertypes.Completion.quickmark_by_name] = model
@pyqtSlot()
def init_bookmark_completions():
"""Initialize bookmark completion models."""
log.completion.debug("Initializing bookmark completion.")
@ -108,7 +104,6 @@ def init_bookmark_completions():
_instances[usertypes.Completion.bookmark_by_url] = model
@pyqtSlot()
def init_session_completion():
"""Initialize session completion model."""
log.completion.debug("Initializing session completion.")

View File

@ -33,7 +33,7 @@ import contextlib
import collections
import collections.abc
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QSettings
from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QSettings
from qutebrowser.config import configdata, configexc, textwrapper
from qutebrowser.config.parsers import ini, keyconf
@ -94,8 +94,6 @@ class change_filter: # pylint: disable=invalid-name
The decorated function.
"""
if self._function:
@pyqtSlot(str, str)
@pyqtSlot()
@functools.wraps(func)
def wrapper(sectname=None, optname=None):
if sectname is None and optname is None:
@ -108,8 +106,6 @@ class change_filter: # pylint: disable=invalid-name
else:
return func()
else:
@pyqtSlot(str, str)
@pyqtSlot()
@functools.wraps(func)
def wrapper(wrapper_self, sectname=None, optname=None):
if sectname is None and optname is None:
@ -346,8 +342,8 @@ class ConfigManager(QObject):
DELETED_OPTIONS = [
('colors', 'tab.separator'),
('colors', 'tabs.separator'),
('colors', 'tab.seperator'),
('colors', 'tabs.seperator'),
('colors', 'tab.seperator'), # pragma: no spellcheck
('colors', 'tabs.seperator'), # pragma: no spellcheck
('colors', 'completion.item.bg'),
('tabs', 'indicator-space'),
('tabs', 'hide-auto'),

View File

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block style %}
table { border: 1px solid grey; border-collapse: collapse; width: 100%;}
th, td { border: 1px solid grey; padding: 0px 5px; }
th { background: lightgrey; }
{% endblock %}
{% block content %}
<table>
<tr>
<th><h3>Bookmark</h3></th>
<th><h3>URL</h3></th>
</tr>
{% for url, title in bookmarks %}
<tr>
<td><a href="{{url}}">{{title}}</a></td>
<td>{{url}}</td>
</tr>
{% endfor %}
<tr>
<th><h3>Quickmark</h3></th>
<th><h3>URL</h3></th>
</tr>
{% for name, url in quickmarks %}
<tr>
<td><a href="{{url}}">{{name}}</a></td>
<td>{{url}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -38,3 +38,30 @@ function scroll_delta_page(x, y) {
var dy = document.documentElement.clientHeight * y;
window.scrollBy(dx, dy);
}
function scroll_pos() {
var elem = document.documentElement;
var dx = (elem.scrollWidth - elem.clientWidth);
var dy = (elem.scrollHeight - elem.clientHeight);
var perc_x, perc_y;
if (dx === 0) {
perc_x = 0;
} else {
perc_x = 100 / dx * window.scrollX;
}
if (dy === 0) {
perc_y = 0;
} else {
perc_y = 100 / dy * window.scrollY;
}
var pos_perc = {'x': perc_x, 'y': perc_y};
var pos_px = {'x': window.scrollX, 'y': window.scrollY};
var pos = {'perc': pos_perc, 'px': pos_px};
// console.log(JSON.stringify(pos));
return pos;
}

View File

@ -673,7 +673,6 @@ class TabbedBrowser(tabwidget.TabWidget):
if key in self._global_marks:
point, url = self._global_marks[key]
@pyqtSlot(bool)
def callback(ok):
if ok:
self.cur_load_finished.disconnect(callback)

View File

@ -477,29 +477,6 @@ class ExceptionCrashDialog(_CrashDialog):
else:
self.reject()
@pyqtSlot()
def on_report_clicked(self):
"""Ignore reports with the QtWebEngine backend.
FIXME:qtwebengine Remove this when QtWebEngine is working better!
"""
try:
backend = objreg.get('args').backend
except Exception:
backend = 'webkit'
if backend == 'webkit':
super().on_report_clicked()
return
title = "Crash reports disabled with QtWebEngine!"
text = ("You're using the QtWebEngine backend which is not intended "
"for general usage yet. Crash reports with that backend have "
"been disabled.")
box = msgbox.msgbox(parent=self, title=title, text=text,
icon=QMessageBox.Critical)
box.finished.connect(self.finish)
class FatalCrashDialog(_CrashDialog):

View File

@ -309,13 +309,14 @@ def earlyinit(args):
# Here we check if QtCore is available, and if not, print a message to the
# console or via Tk.
check_pyqt_core()
# Init logging as early as possible
init_log(args)
# Now the faulthandler is enabled we fix the Qt harfbuzzing library, before
# importing QtWidgets.
fix_harfbuzz(args)
# Now we can be sure QtCore is available, so we can print dialogs on
# errors, so people only using the GUI notice them as well.
check_qt_version()
check_ssl_support()
remove_inputhook()
check_libraries(args)
init_log(args)
check_ssl_support()

View File

@ -67,7 +67,6 @@ class Saveable:
save_on_exit=self._save_on_exit,
filename=self._filename)
@pyqtSlot()
def mark_dirty(self):
"""Mark this saveable as dirty (having changes)."""
log.save.debug("Marking {} as dirty.".format(self._name))

View File

@ -167,9 +167,14 @@ def init_log(args):
root.addHandler(ram)
root.setLevel(logging.NOTSET)
logging.captureWarnings(True)
_init_py_warnings()
QtCore.qInstallMessageHandler(qt_message_handler)
def _init_py_warnings():
"""Initialize Python warning handling."""
warnings.simplefilter('default')
warnings.filterwarnings('ignore', module='pdb', category=ResourceWarning)
QtCore.qInstallMessageHandler(qt_message_handler)
@contextlib.contextmanager
@ -182,6 +187,14 @@ def disable_qt_msghandler():
QtCore.qInstallMessageHandler(old_handler)
@contextlib.contextmanager
def ignore_py_warnings(**kwargs):
"""Contextmanager to temporarily hke certain Python warnings."""
warnings.filterwarnings('ignore', **kwargs)
yield
_init_py_warnings()
def _init_handlers(level, color, force_color, json_logging, ram_capacity):
"""Init log handlers.
@ -330,6 +343,10 @@ def qt_message_handler(msg_type, context, msg):
"Image of format '' blocked because it is not considered safe. If you "
"are sure it is safe to do so, you can white-list the format by "
"setting the environment variable QTWEBKIT_IMAGEFORMAT_WHITELIST=",
# Installing Qt from the installer may cause it looking for SSL3 which
# may not be available on the system
"QSslSocket: cannot resolve SSLv3_client_method",
"QSslSocket: cannot resolve SSLv3_server_method",
]
if sys.platform == 'darwin':
suppressed_msgs += [

View File

@ -26,7 +26,7 @@ import datetime
import collections
import traceback
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication
from qutebrowser.utils import usertypes, log, objreg, utils
@ -108,7 +108,6 @@ def _get_bridge(win_id):
return objreg.get('message-bridge', scope='window', window=win_id)
@pyqtSlot()
def on_focus_changed():
"""Show queued messages when a new window has been focused.

View File

@ -33,11 +33,21 @@ import sys
import operator
import contextlib
import pkg_resources
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QSaveFile)
from PyQt5.QtWidgets import QApplication
from qutebrowser.utils import log
with log.ignore_py_warnings(category=PendingDeprecationWarning, module='imp'):
with log.ignore_py_warnings(category=ImportWarning):
# This imports 'imp' and gives us a PendingDeprecationWarning on
# Debian Jessie.
#
# On Archlinux, we get ImportWarning from
# importlib/_bootstrap_external.py for modules with missing __init__.
import pkg_resources
MAXVALS = {
'int': 2 ** 31 - 1,

View File

@ -34,7 +34,8 @@ class FakeTypingMeta(type):
"""Fake typing metaclass like typing.TypingMeta."""
def __init__(self, *args, **kwds): # pylint: disable=super-init-not-called
def __init__(self, *args, # pylint: disable=super-init-not-called
**_kwds):
pass
def __subclasscheck__(self, cls):

View File

@ -31,11 +31,6 @@ from __future__ import print_function
import subprocess
import urllib
try:
import _winreg as winreg
except ImportError:
winreg = None
def check_setup(executable):
subprocess.check_call([executable, '-c', 'import PyQt5'])
@ -56,12 +51,6 @@ pyqt_url = ('http://www.qutebrowser.org/pyqt/'
pyqt_version, qt_version))
urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe')
print("Fixing registry...")
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r'Software\Python\PythonCore\3.4', 0,
winreg.KEY_WRITE) as key:
winreg.SetValue(key, 'InstallPath', winreg.REG_SZ, r'C:\Python34')
print("Installing PyQt5...")
subprocess.check_call([r'C:\install-PyQt5.exe', '/S'])

View File

@ -56,7 +56,7 @@ def get_build_exe_options():
opts['includes'] += pytest.freeze_includes() # pylint: disable=no-member
opts['includes'] += ['unittest.mock', 'PyQt5.QtTest', 'hypothesis', 'bs4',
'httpbin', 'jinja2.ext', 'cherrypy.wsgiserver',
'cherrypy.wsgiserver.wsgiserver3', 'pstats']
'pstats']
httpbin_dir = os.path.dirname(httpbin.__file__)
opts['include_files'] += [

View File

@ -112,7 +112,9 @@ def check_spelling():
continue
for line in f:
for w in words:
if re.search(w, line) and fn not in seen[w]:
if (re.search(w, line) and
fn not in seen[w] and
'# pragma: no spellcheck' not in line):
print('Found "{}" in {}!'.format(w, fn))
seen[w].append(fn)
ok = False

View File

@ -238,3 +238,13 @@ Feature: Using hints
And I press the key "2"
And I wait for "Leaving mode KeyMode.hint (reason: all filtered)" in the log
Then no crash should happen
# https://github.com/The-Compiler/qutebrowser/issues/1657
Scenario: Using rapid number hinting twice
When I open data/hints/number.html
And I set hints -> mode to number
And I run :hint --rapid
And I run :leave-mode
And I run :hint --rapid
And I run :follow-hint 00
Then data/numbers/1.txt should be loaded

View File

@ -208,3 +208,16 @@ Feature: quickmarks and bookmarks
And I run :quickmark-add http://localhost:(port)/data/numbers/1.txt seventeen
And I run :quickmark-del
Then the quickmark file should not contain "seventeen http://localhost:*/data/numbers/1.txt"
Scenario: Listing quickmarks
When I run :quickmark-add http://localhost:(port)/data/numbers/15.txt fifteen
And I run :quickmark-add http://localhost:(port)/data/numbers/14.txt fourteen
And I open qute:bookmarks
Then the page should contain the plaintext "fifteen"
And the page should contain the plaintext "fourteen"
Scenario: Listing bookmarks
When I open data/title.html
And I run :bookmark-add
And I open qute:bookmarks
Then the page should contain the plaintext "Test title"

View File

@ -106,9 +106,6 @@ class WSGIServer(cherrypy.wsgiserver.CherryPyWSGIServer):
_printed_ready: Whether the initial ready message was printed.
"""
# pylint: disable=no-member
# WORKAROUND for https://bitbucket.org/logilab/pylint/issues/702
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._ready = False

View File

@ -30,6 +30,7 @@ import itertools
import textwrap
import unittest.mock
import types
import os
import pytest
@ -405,3 +406,40 @@ def mode_manager(win_registry, config_stub, qapp):
objreg.register('mode-manager', mm, scope='window', window=0)
yield mm
objreg.delete('mode-manager', scope='window', window=0)
@pytest.fixture
def config_tmpdir(monkeypatch, tmpdir):
"""Set tmpdir/config as the configdir.
Use this to avoid creating a 'real' config dir (~/.config/qute_test).
"""
confdir = tmpdir / 'config'
path = str(confdir)
os.mkdir(path)
monkeypatch.setattr('qutebrowser.utils.standarddir.config', lambda: path)
return confdir
@pytest.fixture
def data_tmpdir(monkeypatch, tmpdir):
"""Set tmpdir/data as the datadir.
Use this to avoid creating a 'real' data dir (~/.local/share/qute_test).
"""
datadir = tmpdir / 'data'
path = str(datadir)
os.mkdir(path)
monkeypatch.setattr('qutebrowser.utils.standarddir.data', lambda: path)
return datadir
@pytest.fixture
def redirect_xdg_data(data_tmpdir, monkeypatch):
"""Set XDG_DATA_HOME to a temp location.
While data_tmpdir covers most cases by redirecting standarddir.data(), this
is not enough for places Qt references the data dir internally. For these,
we need to set the environment variable to redirect data access.
"""
monkeypatch.setenv('XDG_DATA_HOME', str(data_tmpdir))

View File

@ -46,6 +46,9 @@ class LogFailHandler(logging.Handler):
logger = logging.getLogger(record.name)
root_logger = logging.getLogger()
if logger.name == 'messagemock':
return
for h in root_logger.handlers:
if isinstance(h, catchlog_mod.LogCaptureHandler):
catchlog_handler = h

View File

@ -62,9 +62,7 @@ class MessageMock:
}
log_level = log_levels[level]
with self._caplog.at_level(log_level): # needed so we don't fail
logging.getLogger('message').log(log_level, text)
logging.getLogger('messagemock').log(log_level, text)
self.messages.append(Message(level, win_id, text, immediately))
def _handle_error(self, *args, **kwargs):

View File

@ -30,7 +30,7 @@ from qutebrowser.browser import adblock
from qutebrowser.utils import objreg
from qutebrowser.commands import cmdexc
pytestmark = pytest.mark.usefixtures('qapp')
pytestmark = pytest.mark.usefixtures('qapp', 'config_tmpdir')
# TODO See ../utils/test_standarddirutils for OSError and caplog assertion
@ -54,13 +54,6 @@ URLS_TO_CHECK = ('http://localhost',
'http://qutebrowser.org')
@pytest.fixture
def data_tmpdir(monkeypatch, tmpdir):
"""Set tmpdir as datadir."""
tmpdir = str(tmpdir)
monkeypatch.setattr('qutebrowser.utils.standarddir.data', lambda: tmpdir)
class BaseDirStub:
"""Mock for objreg.get('args') called in adblock.HostBlocker.read_hosts."""
@ -348,7 +341,7 @@ def test_blocking_with_whitelist(config_stub, basedir, download_stub,
# by creating a file named blocked-hosts,
# Exclude localhost from it, since localhost is in HostBlocker.WHITELISTED
filtered_blocked_hosts = BLOCKLIST_HOSTS[1:]
blocklist = create_blocklist(tmpdir,
blocklist = create_blocklist(data_tmpdir,
blocked_hosts=filtered_blocked_hosts,
name='blocked-hosts',
line_format='one_per_line')

View File

@ -25,6 +25,8 @@ from qutebrowser.browser import browsertab
from qutebrowser.keyinput import modeman
from qutebrowser.utils import objreg
pytestmark = pytest.mark.usefixtures('redirect_xdg_data')
try:
from PyQt5.QtWebKitWidgets import QWebView

View File

@ -24,6 +24,8 @@ import pytest
from qutebrowser.browser.webkit import cookies
from qutebrowser.misc import lineparser
pytestmark = pytest.mark.usefixtures('data_tmpdir')
CONFIG_ALL_COOKIES = {'content': {'cookies-accept': 'all'}}
CONFIG_NEVER_COOKIES = {'content': {'cookies-accept': 'never'}}
CONFIG_COOKIES_ENABLED = {'content': {'cookies-store': True}}

View File

@ -64,6 +64,7 @@ def test_element_js_webkit(webview, js_enabled, expected):
assert result == expected
@pytest.mark.usefixtures('redirect_xdg_data')
@pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, 2.0)])
def test_simple_js_webengine(qtbot, webengineview, js_enabled, expected):
"""With QtWebEngine, runJavaScript works even when JS is off."""

View File

@ -295,6 +295,7 @@ class TestKeyConfigParser:
assert new == new_expected
@pytest.mark.usefixtures('config_tmpdir')
@pytest.mark.integration
class TestDefaultConfig:

View File

@ -1282,6 +1282,7 @@ def unrequired_class(**kwargs):
@pytest.mark.usefixtures('qapp')
@pytest.mark.usefixtures('config_tmpdir')
class TestFileAndUserStyleSheet:
"""Test File/UserStyleSheet."""

View File

@ -42,7 +42,7 @@ def gen_classes():
yield member
@pytest.mark.usefixtures('qapp')
@pytest.mark.usefixtures('qapp', 'config_tmpdir')
@pytest.mark.parametrize('klass', gen_classes())
@hypothesis.given(strategies.text())
@hypothesis.example('\x00')

View File

@ -24,7 +24,7 @@ import pytest
from qutebrowser.misc import msgbox
from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QWidget
@ -64,7 +64,6 @@ def test_finished_signal(qtbot):
"""Make sure we can pass a slot to be called when the dialog finished."""
signal_triggered = False
@pyqtSlot()
def on_finished():
nonlocal signal_triggered
signal_triggered = True

View File

@ -26,7 +26,7 @@ import pytest
from qutebrowser.utils import error
from qutebrowser.misc import ipc
from PyQt5.QtCore import pyqtSlot, QTimer
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMessageBox
@ -81,7 +81,6 @@ def test_no_err_windows(caplog, exc, name, exc_text, fake_args):
], ids=repr)
def test_err_windows(qtbot, qapp, fake_args, pre_text, post_text, expected):
@pyqtSlot()
def err_window_check():
w = qapp.activeModalWidget()
try:

View File

@ -23,6 +23,7 @@ import logging
import argparse
import itertools
import sys
import warnings
import pytest
import pytest_catchlog
@ -36,6 +37,7 @@ def restore_loggers():
Based on CPython's Lib/test/test_logging.py.
"""
logging.captureWarnings(False)
logger_dict = logging.getLogger().manager.loggerDict
logging._acquireLock()
try:
@ -278,3 +280,14 @@ def test_stub(caplog, suffix, expected):
log.stub(suffix)
assert len(caplog.records) == 1
assert caplog.records[0].message == expected
def test_ignore_py_warnings(caplog):
logging.captureWarnings(True)
with log.ignore_py_warnings(category=UserWarning):
warnings.warn("hidden", UserWarning)
with caplog.at_level(logging.WARNING):
warnings.warn("not hidden", UserWarning)
assert len(caplog.records) == 1
msg = caplog.records[0].message.splitlines()[0]
assert msg.endswith("UserWarning: not hidden")

View File

@ -52,15 +52,15 @@ def no_cachedir_tag(monkeypatch):
lambda: None)
@pytest.yield_fixture(autouse=True)
@pytest.mark.usefixtures('no_cachedir_tag')
def reset_standarddir():
@pytest.yield_fixture
def reset_standarddir(no_cachedir_tag):
"""Clean up standarddir arguments before and after each test."""
standarddir.init(None)
yield
standarddir.init(None)
@pytest.mark.usefixtures('reset_standarddir')
@pytest.mark.parametrize('data_subdir, config_subdir, expected', [
('foo', 'foo', 'foo/data'),
('foo', 'bar', 'foo'),
@ -80,6 +80,7 @@ def test_get_fake_windows_equal_dir(data_subdir, config_subdir, expected,
assert standarddir.data() == expected
@pytest.mark.usefixtures('reset_standarddir')
class TestWritableLocation:
"""Tests for _writable_location."""
@ -100,7 +101,7 @@ class TestWritableLocation:
assert '\\' in loc
@pytest.mark.usefixtures('no_cachedir_tag')
@pytest.mark.usefixtures('reset_standarddir')
class TestStandardDir:
"""Tests for standarddir."""
@ -159,7 +160,7 @@ class TestStandardDir:
DirArgTest = collections.namedtuple('DirArgTest', 'arg, expected')
@pytest.mark.usefixtures('no_cachedir_tag')
@pytest.mark.usefixtures('reset_standarddir')
class TestArguments:
"""Tests with confdir/cachedir/datadir arguments."""
@ -195,8 +196,10 @@ class TestArguments:
standarddir.init(args)
assert standarddir.data() == testcase.expected
def test_confdir_none(self):
def test_confdir_none(self, mocker):
"""Test --confdir with None given."""
# patch makedirs to a noop so we don't really create a directory
mocker.patch('qutebrowser.utils.standarddir.os.makedirs')
args = types.SimpleNamespace(confdir=None, cachedir=None, datadir=None,
basedir=None)
standarddir.init(args)
@ -294,6 +297,7 @@ class TestCreatingDir:
if os.name == 'posix':
assert basedir.stat().mode & 0o777 == 0o700
@pytest.mark.usefixtures('reset_standarddir')
@pytest.mark.parametrize('typ', DIR_TYPES)
def test_exists_race_condition(self, mocker, tmpdir, typ):
"""Make sure there can't be a TOCTOU issue when creating the file.
@ -315,6 +319,7 @@ class TestCreatingDir:
func()
@pytest.mark.usefixtures('reset_standarddir')
class TestSystemData:
"""Test system data path."""
@ -326,12 +331,18 @@ class TestSystemData:
assert standarddir.system_data() == "/usr/share/qutebrowser"
@pytest.mark.linux
def test_system_datadir_not_exist_linux(self, monkeypatch):
def test_system_datadir_not_exist_linux(self, monkeypatch, tmpdir,
fake_args):
"""Test that system-wide path isn't used on linux if path not exist."""
fake_args.basedir = str(tmpdir)
standarddir.init(fake_args)
monkeypatch.setattr(os.path, 'exists', lambda path: False)
assert standarddir.system_data() == standarddir.data()
def test_system_datadir_unsupportedos(self, monkeypatch):
def test_system_datadir_unsupportedos(self, monkeypatch, tmpdir,
fake_args):
"""Test that system-wide path is not used on non-Linux OS."""
fake_args.basedir = str(tmpdir)
standarddir.init(fake_args)
monkeypatch.setattr('sys.platform', "potato")
assert standarddir.system_data() == standarddir.data()

View File

@ -5,6 +5,7 @@
[tox]
envlist = py34,py35-cov,misc,vulture,flake8,pylint,pyroma,check-manifest
distshare = {toxworkdir}
[testenv]
# https://bitbucket.org/hpk42/tox/issue/246/ - only needed for Windows though