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] [flake8]
exclude = .venv,.hypothesis,.git,__pycache__,resources.py exclude = .*,__pycache__,resources.py
# E128: continuation line under-indented for visual indent # E128: continuation line under-indented for visual indent
# E226: missing whitespace around arithmetic operator # E226: missing whitespace around arithmetic operator
# E265: Block comment should start with '#' # E265: Block comment should start with '#'

View File

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

View File

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

View File

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

View File

@ -120,7 +120,7 @@ Syntax: +:bookmark-add ['url'] ['title']+
Save the current page as a bookmark, or a specific url. 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 ==== positional arguments
* +'url'+: url to save as a bookmark. If None, use url of current page. * +'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. Add a new quickmark.
You can view all saved quickmarks on the link:qute://bookmarks[bookmarks page].
==== positional arguments ==== positional arguments
* +'url'+: The url to add as quickmark. * +'url'+: The url to add as quickmark.
* +'name'+: The name for the new 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_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og' $ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
$ cd qt5 $ 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 $ cd ../pyqt5
$ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug $ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug
---- ----
@ -76,7 +76,7 @@ Server = http://qutebrowser.org/qt-debug/$arch
Then install the packages: 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 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 isort==4.2.5
lazy-object-proxy==1.2.2 lazy-object-proxy==1.2.2
mccabe==0.5.0 mccabe==0.5.0
pylint==1.6.1 pylint==1.6.4
./scripts/dev/pylint_checkers ./scripts/dev/pylint_checkers
requests==2.10.0 requests==2.10.0
six==1.10.0 six==1.10.0

View File

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

View File

@ -1,3 +1,3 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py # 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> # 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 # 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 # 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 # 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() { help() {
blink=$'\e[1;31m' reset=$'\e[0m' blink=$'\e[1;31m' reset=$'\e[0m'
cat <<EOF cat <<EOF

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#!/bin/bash -e #!/usr/bin/env bash -e
# #
# Behavior: # Behavior:
# Userscript for qutebrowser which views the current web page in mpv using # 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__ state_config['general']['version'] = qutebrowser.__version__
@pyqtSlot('QWidget*', 'QWidget*')
def on_focus_changed(_old, new): def on_focus_changed(_old, new):
"""Register currently focused main window in the object registry.""" """Register currently focused main window in the object registry."""
if not isinstance(new, QWidget): if not isinstance(new, QWidget):
@ -355,7 +354,6 @@ def on_focus_changed(_old, new):
_maybe_hide_mouse_cursor() _maybe_hide_mouse_cursor()
@pyqtSlot(QUrl)
def open_desktopservices_url(url): def open_desktopservices_url(url):
"""Handler to open a URL via QDesktopServices.""" """Handler to open a URL via QDesktopServices."""
win_id = mainwindow.get_window(via_ipc=True, force_window=False) win_id = mainwindow.get_window(via_ipc=True, force_window=False)
@ -476,7 +474,6 @@ class Quitter:
self._shutting_down = False self._shutting_down = False
self._args = args self._args = args
@pyqtSlot()
def on_last_window_closed(self): def on_last_window_closed(self):
"""Slot which gets invoked when the last window was closed.""" """Slot which gets invoked when the last window was closed."""
self.shutdown(last_window=True) self.shutdown(last_window=True)

View File

@ -24,7 +24,6 @@ import itertools
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QLayout from PyQt5.QtWidgets import QWidget, QLayout
from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.config import config from qutebrowser.config import config
@ -122,7 +121,6 @@ class AbstractPrinting:
def to_pdf(self, filename): def to_pdf(self, filename):
raise NotImplementedError raise NotImplementedError
@pyqtSlot(QPrinter)
def to_printer(self, printer): def to_printer(self, printer):
raise NotImplementedError raise NotImplementedError
@ -354,6 +352,9 @@ class AbstractScroller(QObject):
self._tab = tab self._tab = tab
self._widget = None self._widget = None
def _init_widget(self, widget):
self._widget = widget
def pos_px(self): def pos_px(self):
raise NotImplementedError raise NotImplementedError
@ -513,7 +514,7 @@ class AbstractTab(QWidget):
self._layout = WrapperLayout(widget, self) self._layout = WrapperLayout(widget, self)
self._widget = widget self._widget = widget
self.history._history = widget.history() self.history._history = widget.history()
self.scroll._widget = widget self.scroll._init_widget(widget)
self.caret._widget = widget self.caret._widget = widget
self.zoom._widget = widget self.zoom._widget = widget
self.search._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 If a url and title have been provided, then save the given url as
a bookmark with the provided title. a bookmark with the provided title.
You can view all saved bookmarks on the
link:qute://bookmarks[bookmarks page].
Args: Args:
url: url to save as a bookmark. If None, use url of current page. url: url to save as a bookmark. If None, use url of current page.
title: title of the new bookmark. title: title of the new bookmark.

View File

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

View File

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

View File

@ -25,7 +25,6 @@
from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint
from PyQt5.QtGui import QKeyEvent, QIcon from PyQt5.QtGui import QKeyEvent, QIcon
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtPrintSupport import QPrinter
# pylint: disable=no-name-in-module,import-error,useless-suppression # pylint: disable=no-name-in-module,import-error,useless-suppression
from PyQt5.QtWebEngineWidgets import QWebEnginePage from PyQt5.QtWebEngineWidgets import QWebEnginePage
# pylint: enable=no-name-in-module,import-error,useless-suppression # pylint: enable=no-name-in-module,import-error,useless-suppression
@ -51,7 +50,6 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
def to_pdf(self, filename): def to_pdf(self, filename):
self._widget.page().printToPdf(filename) self._widget.page().printToPdf(filename)
@pyqtSlot(QPrinter)
def to_printer(self, printer): def to_printer(self, printer):
# Should never be called # Should never be called
assert False assert False
@ -182,6 +180,21 @@ class WebEngineScroller(browsertab.AbstractScroller):
"""QtWebEngine implementations related to scrolling.""" """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): def _key_press(self, key, count=1):
# FIXME:qtwebengine Abort scrolling if the minimum/maximum was reached. # FIXME:qtwebengine Abort scrolling if the minimum/maximum was reached.
press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0) 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, press_evt)
QApplication.postEvent(recipient, release_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): def pos_px(self):
log.stub() return self._pos_px
return QPoint(0, 0)
def pos_perc(self): def pos_perc(self):
page = self._widget.page() return self._pos_perc
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)
def to_perc(self, x=None, y=None): def to_perc(self, x=None, y=None):
js_code = """ js_code = """
@ -260,7 +276,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
self._key_press(Qt.Key_PageDown, count) self._key_press(Qt.Key_PageDown, count)
def at_top(self): def at_top(self):
log.stub() return self.pos_px().y() == 0
def at_bottom(self): def at_bottom(self):
log.stub() log.stub()
@ -409,5 +425,3 @@ class WebEngineTab(browsertab.AbstractTab):
view.iconChanged.connect(self.icon_changed) view.iconChanged.connect(self.icon_changed)
except AttributeError: except AttributeError:
log.stub('iconChanged, on Qt < 5.7') 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)) "pdfjs resource requested but not found: {}".format(e.path))
raise QuteSchemeError("Can't find pdfjs resource '{}'".format(e.path), raise QuteSchemeError("Can't find pdfjs resource '{}'".format(e.path),
QNetworkReply.ContentNotFoundError) 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) printer.setOutputFileName(filename)
self.to_printer(printer) self.to_printer(printer)
@pyqtSlot(QPrinter)
def to_printer(self, printer): def to_printer(self, printer):
self._widget.print(printer) self._widget.print(printer)

View File

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

View File

@ -33,7 +33,7 @@ import contextlib
import collections import collections
import collections.abc 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 import configdata, configexc, textwrapper
from qutebrowser.config.parsers import ini, keyconf from qutebrowser.config.parsers import ini, keyconf
@ -94,8 +94,6 @@ class change_filter: # pylint: disable=invalid-name
The decorated function. The decorated function.
""" """
if self._function: if self._function:
@pyqtSlot(str, str)
@pyqtSlot()
@functools.wraps(func) @functools.wraps(func)
def wrapper(sectname=None, optname=None): def wrapper(sectname=None, optname=None):
if sectname is None and optname is None: if sectname is None and optname is None:
@ -108,8 +106,6 @@ class change_filter: # pylint: disable=invalid-name
else: else:
return func() return func()
else: else:
@pyqtSlot(str, str)
@pyqtSlot()
@functools.wraps(func) @functools.wraps(func)
def wrapper(wrapper_self, sectname=None, optname=None): def wrapper(wrapper_self, sectname=None, optname=None):
if sectname is None and optname is None: if sectname is None and optname is None:
@ -346,8 +342,8 @@ class ConfigManager(QObject):
DELETED_OPTIONS = [ DELETED_OPTIONS = [
('colors', 'tab.separator'), ('colors', 'tab.separator'),
('colors', 'tabs.separator'), ('colors', 'tabs.separator'),
('colors', 'tab.seperator'), ('colors', 'tab.seperator'), # pragma: no spellcheck
('colors', 'tabs.seperator'), ('colors', 'tabs.seperator'), # pragma: no spellcheck
('colors', 'completion.item.bg'), ('colors', 'completion.item.bg'),
('tabs', 'indicator-space'), ('tabs', 'indicator-space'),
('tabs', 'hide-auto'), ('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; var dy = document.documentElement.clientHeight * y;
window.scrollBy(dx, dy); 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: if key in self._global_marks:
point, url = self._global_marks[key] point, url = self._global_marks[key]
@pyqtSlot(bool)
def callback(ok): def callback(ok):
if ok: if ok:
self.cur_load_finished.disconnect(callback) self.cur_load_finished.disconnect(callback)

View File

@ -477,29 +477,6 @@ class ExceptionCrashDialog(_CrashDialog):
else: else:
self.reject() 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): 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 # Here we check if QtCore is available, and if not, print a message to the
# console or via Tk. # console or via Tk.
check_pyqt_core() check_pyqt_core()
# Init logging as early as possible
init_log(args)
# Now the faulthandler is enabled we fix the Qt harfbuzzing library, before # Now the faulthandler is enabled we fix the Qt harfbuzzing library, before
# importing QtWidgets. # importing QtWidgets.
fix_harfbuzz(args) fix_harfbuzz(args)
# Now we can be sure QtCore is available, so we can print dialogs on # 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. # errors, so people only using the GUI notice them as well.
check_qt_version() check_qt_version()
check_ssl_support()
remove_inputhook() remove_inputhook()
check_libraries(args) check_libraries(args)
init_log(args) check_ssl_support()

View File

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

View File

@ -167,9 +167,14 @@ def init_log(args):
root.addHandler(ram) root.addHandler(ram)
root.setLevel(logging.NOTSET) root.setLevel(logging.NOTSET)
logging.captureWarnings(True) logging.captureWarnings(True)
_init_py_warnings()
QtCore.qInstallMessageHandler(qt_message_handler)
def _init_py_warnings():
"""Initialize Python warning handling."""
warnings.simplefilter('default') warnings.simplefilter('default')
warnings.filterwarnings('ignore', module='pdb', category=ResourceWarning) warnings.filterwarnings('ignore', module='pdb', category=ResourceWarning)
QtCore.qInstallMessageHandler(qt_message_handler)
@contextlib.contextmanager @contextlib.contextmanager
@ -182,6 +187,14 @@ def disable_qt_msghandler():
QtCore.qInstallMessageHandler(old_handler) 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): def _init_handlers(level, color, force_color, json_logging, ram_capacity):
"""Init log handlers. """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 " "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 " "are sure it is safe to do so, you can white-list the format by "
"setting the environment variable QTWEBKIT_IMAGEFORMAT_WHITELIST=", "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': if sys.platform == 'darwin':
suppressed_msgs += [ suppressed_msgs += [

View File

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

View File

@ -33,11 +33,21 @@ import sys
import operator import operator
import contextlib import contextlib
import pkg_resources
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray, from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QSaveFile) QIODevice, QSaveFile)
from PyQt5.QtWidgets import QApplication 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 = { MAXVALS = {
'int': 2 ** 31 - 1, 'int': 2 ** 31 - 1,

View File

@ -34,7 +34,8 @@ class FakeTypingMeta(type):
"""Fake typing metaclass like typing.TypingMeta.""" """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 pass
def __subclasscheck__(self, cls): def __subclasscheck__(self, cls):

View File

@ -31,11 +31,6 @@ from __future__ import print_function
import subprocess import subprocess
import urllib import urllib
try:
import _winreg as winreg
except ImportError:
winreg = None
def check_setup(executable): def check_setup(executable):
subprocess.check_call([executable, '-c', 'import PyQt5']) subprocess.check_call([executable, '-c', 'import PyQt5'])
@ -56,12 +51,6 @@ pyqt_url = ('http://www.qutebrowser.org/pyqt/'
pyqt_version, qt_version)) pyqt_version, qt_version))
urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe') 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...") print("Installing PyQt5...")
subprocess.check_call([r'C:\install-PyQt5.exe', '/S']) 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'] += pytest.freeze_includes() # pylint: disable=no-member
opts['includes'] += ['unittest.mock', 'PyQt5.QtTest', 'hypothesis', 'bs4', opts['includes'] += ['unittest.mock', 'PyQt5.QtTest', 'hypothesis', 'bs4',
'httpbin', 'jinja2.ext', 'cherrypy.wsgiserver', 'httpbin', 'jinja2.ext', 'cherrypy.wsgiserver',
'cherrypy.wsgiserver.wsgiserver3', 'pstats'] 'pstats']
httpbin_dir = os.path.dirname(httpbin.__file__) httpbin_dir = os.path.dirname(httpbin.__file__)
opts['include_files'] += [ opts['include_files'] += [

View File

@ -112,7 +112,9 @@ def check_spelling():
continue continue
for line in f: for line in f:
for w in words: 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)) print('Found "{}" in {}!'.format(w, fn))
seen[w].append(fn) seen[w].append(fn)
ok = False ok = False

View File

@ -238,3 +238,13 @@ Feature: Using hints
And I press the key "2" And I press the key "2"
And I wait for "Leaving mode KeyMode.hint (reason: all filtered)" in the log And I wait for "Leaving mode KeyMode.hint (reason: all filtered)" in the log
Then no crash should happen 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-add http://localhost:(port)/data/numbers/1.txt seventeen
And I run :quickmark-del And I run :quickmark-del
Then the quickmark file should not contain "seventeen http://localhost:*/data/numbers/1.txt" 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. _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): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._ready = False self._ready = False

View File

@ -30,6 +30,7 @@ import itertools
import textwrap import textwrap
import unittest.mock import unittest.mock
import types import types
import os
import pytest import pytest
@ -405,3 +406,40 @@ def mode_manager(win_registry, config_stub, qapp):
objreg.register('mode-manager', mm, scope='window', window=0) objreg.register('mode-manager', mm, scope='window', window=0)
yield mm yield mm
objreg.delete('mode-manager', scope='window', window=0) 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) logger = logging.getLogger(record.name)
root_logger = logging.getLogger() root_logger = logging.getLogger()
if logger.name == 'messagemock':
return
for h in root_logger.handlers: for h in root_logger.handlers:
if isinstance(h, catchlog_mod.LogCaptureHandler): if isinstance(h, catchlog_mod.LogCaptureHandler):
catchlog_handler = h catchlog_handler = h

View File

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

View File

@ -30,7 +30,7 @@ from qutebrowser.browser import adblock
from qutebrowser.utils import objreg from qutebrowser.utils import objreg
from qutebrowser.commands import cmdexc 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 # TODO See ../utils/test_standarddirutils for OSError and caplog assertion
@ -54,13 +54,6 @@ URLS_TO_CHECK = ('http://localhost',
'http://qutebrowser.org') '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: class BaseDirStub:
"""Mock for objreg.get('args') called in adblock.HostBlocker.read_hosts.""" """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, # by creating a file named blocked-hosts,
# Exclude localhost from it, since localhost is in HostBlocker.WHITELISTED # Exclude localhost from it, since localhost is in HostBlocker.WHITELISTED
filtered_blocked_hosts = BLOCKLIST_HOSTS[1:] filtered_blocked_hosts = BLOCKLIST_HOSTS[1:]
blocklist = create_blocklist(tmpdir, blocklist = create_blocklist(data_tmpdir,
blocked_hosts=filtered_blocked_hosts, blocked_hosts=filtered_blocked_hosts,
name='blocked-hosts', name='blocked-hosts',
line_format='one_per_line') line_format='one_per_line')

View File

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

View File

@ -24,6 +24,8 @@ import pytest
from qutebrowser.browser.webkit import cookies from qutebrowser.browser.webkit import cookies
from qutebrowser.misc import lineparser from qutebrowser.misc import lineparser
pytestmark = pytest.mark.usefixtures('data_tmpdir')
CONFIG_ALL_COOKIES = {'content': {'cookies-accept': 'all'}} CONFIG_ALL_COOKIES = {'content': {'cookies-accept': 'all'}}
CONFIG_NEVER_COOKIES = {'content': {'cookies-accept': 'never'}} CONFIG_NEVER_COOKIES = {'content': {'cookies-accept': 'never'}}
CONFIG_COOKIES_ENABLED = {'content': {'cookies-store': True}} 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 assert result == expected
@pytest.mark.usefixtures('redirect_xdg_data')
@pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, 2.0)]) @pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, 2.0)])
def test_simple_js_webengine(qtbot, webengineview, js_enabled, expected): def test_simple_js_webengine(qtbot, webengineview, js_enabled, expected):
"""With QtWebEngine, runJavaScript works even when JS is off.""" """With QtWebEngine, runJavaScript works even when JS is off."""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ import logging
import argparse import argparse
import itertools import itertools
import sys import sys
import warnings
import pytest import pytest
import pytest_catchlog import pytest_catchlog
@ -36,6 +37,7 @@ def restore_loggers():
Based on CPython's Lib/test/test_logging.py. Based on CPython's Lib/test/test_logging.py.
""" """
logging.captureWarnings(False)
logger_dict = logging.getLogger().manager.loggerDict logger_dict = logging.getLogger().manager.loggerDict
logging._acquireLock() logging._acquireLock()
try: try:
@ -278,3 +280,14 @@ def test_stub(caplog, suffix, expected):
log.stub(suffix) log.stub(suffix)
assert len(caplog.records) == 1 assert len(caplog.records) == 1
assert caplog.records[0].message == expected 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) lambda: None)
@pytest.yield_fixture(autouse=True) @pytest.yield_fixture
@pytest.mark.usefixtures('no_cachedir_tag') def reset_standarddir(no_cachedir_tag):
def reset_standarddir():
"""Clean up standarddir arguments before and after each test.""" """Clean up standarddir arguments before and after each test."""
standarddir.init(None) standarddir.init(None)
yield yield
standarddir.init(None) standarddir.init(None)
@pytest.mark.usefixtures('reset_standarddir')
@pytest.mark.parametrize('data_subdir, config_subdir, expected', [ @pytest.mark.parametrize('data_subdir, config_subdir, expected', [
('foo', 'foo', 'foo/data'), ('foo', 'foo', 'foo/data'),
('foo', 'bar', 'foo'), ('foo', 'bar', 'foo'),
@ -80,6 +80,7 @@ def test_get_fake_windows_equal_dir(data_subdir, config_subdir, expected,
assert standarddir.data() == expected assert standarddir.data() == expected
@pytest.mark.usefixtures('reset_standarddir')
class TestWritableLocation: class TestWritableLocation:
"""Tests for _writable_location.""" """Tests for _writable_location."""
@ -100,7 +101,7 @@ class TestWritableLocation:
assert '\\' in loc assert '\\' in loc
@pytest.mark.usefixtures('no_cachedir_tag') @pytest.mark.usefixtures('reset_standarddir')
class TestStandardDir: class TestStandardDir:
"""Tests for standarddir.""" """Tests for standarddir."""
@ -159,7 +160,7 @@ class TestStandardDir:
DirArgTest = collections.namedtuple('DirArgTest', 'arg, expected') DirArgTest = collections.namedtuple('DirArgTest', 'arg, expected')
@pytest.mark.usefixtures('no_cachedir_tag') @pytest.mark.usefixtures('reset_standarddir')
class TestArguments: class TestArguments:
"""Tests with confdir/cachedir/datadir arguments.""" """Tests with confdir/cachedir/datadir arguments."""
@ -195,8 +196,10 @@ class TestArguments:
standarddir.init(args) standarddir.init(args)
assert standarddir.data() == testcase.expected assert standarddir.data() == testcase.expected
def test_confdir_none(self): def test_confdir_none(self, mocker):
"""Test --confdir with None given.""" """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, args = types.SimpleNamespace(confdir=None, cachedir=None, datadir=None,
basedir=None) basedir=None)
standarddir.init(args) standarddir.init(args)
@ -294,6 +297,7 @@ class TestCreatingDir:
if os.name == 'posix': if os.name == 'posix':
assert basedir.stat().mode & 0o777 == 0o700 assert basedir.stat().mode & 0o777 == 0o700
@pytest.mark.usefixtures('reset_standarddir')
@pytest.mark.parametrize('typ', DIR_TYPES) @pytest.mark.parametrize('typ', DIR_TYPES)
def test_exists_race_condition(self, mocker, tmpdir, typ): def test_exists_race_condition(self, mocker, tmpdir, typ):
"""Make sure there can't be a TOCTOU issue when creating the file. """Make sure there can't be a TOCTOU issue when creating the file.
@ -315,6 +319,7 @@ class TestCreatingDir:
func() func()
@pytest.mark.usefixtures('reset_standarddir')
class TestSystemData: class TestSystemData:
"""Test system data path.""" """Test system data path."""
@ -326,12 +331,18 @@ class TestSystemData:
assert standarddir.system_data() == "/usr/share/qutebrowser" assert standarddir.system_data() == "/usr/share/qutebrowser"
@pytest.mark.linux @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.""" """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) monkeypatch.setattr(os.path, 'exists', lambda path: False)
assert standarddir.system_data() == standarddir.data() 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.""" """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") monkeypatch.setattr('sys.platform', "potato")
assert standarddir.system_data() == standarddir.data() assert standarddir.system_data() == standarddir.data()

View File

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