Drop support for Python 3.4

See #2742
This commit is contained in:
Florian Bruhin 2017-09-18 09:41:12 +02:00
parent 852baaa8c3
commit 505321c336
27 changed files with 55 additions and 189 deletions

View File

@ -99,9 +99,7 @@ Requirements
The following software and libraries are required to run qutebrowser: The following software and libraries are required to run qutebrowser:
* http://www.python.org/[Python] 3.4 or newer (3.6 recommended) - note that * http://www.python.org/[Python] 3.5 or newer (3.6 recommended)
support for Python 3.4
https://github.com/qutebrowser/qutebrowser/issues/2742[will be dropped soon].
* http://qt.io/[Qt] 5.7.1 or newer with the following modules: * http://qt.io/[Qt] 5.7.1 or newer with the following modules:
- QtCore / qtbase - QtCore / qtbase
- QtQuick (part of qtbase in some distributions) - QtQuick (part of qtbase in some distributions)

View File

@ -22,7 +22,7 @@ Breaking changes
- (TODO) Support for legacy QtWebKit (before 5.212 which is distributed - (TODO) Support for legacy QtWebKit (before 5.212 which is distributed
independently from Qt) is dropped. independently from Qt) is dropped.
- (TODO) Support for Python 3.4 is dropped. - Support for Python 3.4 is dropped.
- Support for Qt before 5.7.1 and PyQt before 5.7 is dropped. - Support for Qt before 5.7.1 and PyQt before 5.7 is dropped.
- (TODO) New dependency on ruamel.yaml; dropped PyYAML dependency. - (TODO) New dependency on ruamel.yaml; dropped PyYAML dependency.
- (TODO) The QtWebEngine backend is now used by default if available. - (TODO) The QtWebEngine backend is now used by default if available.

View File

@ -180,7 +180,7 @@ In the _scripts/_ subfolder there's a `run_profile.py` which profiles the code
and shows a graphical representation of what takes how much time. and shows a graphical representation of what takes how much time.
It uses the built-in Python It uses the built-in Python
https://docs.python.org/3.4/library/profile.html[cProfile] module and can show https://docs.python.org/3.6/library/profile.html[cProfile] module and can show
the output in four different ways: the output in four different ways:
* Raw profile file (`--profile-tool=none`) * Raw profile file (`--profile-tool=none`)
@ -535,11 +535,11 @@ ____
Setting up a Windows Development Environment Setting up a Windows Development Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Install https://www.python.org/downloads/release/python-344/[Python 3.4] * Install https://www.python.org/downloads/release/python-362/[Python 3.6].
* Install https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.5.1/[PyQt 5.5] * Install PyQt via `pip install PyQt5`
* Create a file at `C:\Windows\system32\python3.bat` with the following content: * Create a file at `C:\Windows\system32\python3.bat` with the following content (adjust the path as necessary):
`@C:\Python34\python %*` `@C:\Python36\python %*`
This will make the Python 3.4 interpreter available as `python3`, which is used by various development scripts. This will make the Python 3.6 interpreter available as `python3`, which is used by various development scripts.
* Install git from the https://git-scm.com/download/win[git-scm downloads page] * Install git from the https://git-scm.com/download/win[git-scm downloads page]
Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead. Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead.
* Clone your favourite qutebrowser repository. * Clone your favourite qutebrowser repository.

View File

@ -17,8 +17,8 @@ running.
Debian Jessie / Ubuntu 14.04 LTS / Linux Mint < 18 Debian Jessie / Ubuntu 14.04 LTS / Linux Mint < 18
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Those distributions only have Python 3.4 and a too old Qt version available. A Those distributions only have Python 3.4 and a too old Qt version available,
newer Qt isn't easily installable on Python 3.4, unfortunately. while qutebrowser requires Python 3.5 and Qt 5.7.1 or newer.
It should be possible to install Python 3.5 e.g. from the It should be possible to install Python 3.5 e.g. from the
https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or via_ipca https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or via_ipca

View File

@ -24,6 +24,7 @@ import sys
import os.path import os.path
import shlex import shlex
import functools import functools
import typing
from PyQt5.QtWidgets import QApplication, QTabBar, QDialog from PyQt5.QtWidgets import QApplication, QTabBar, QDialog
from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery
@ -39,10 +40,11 @@ from qutebrowser.browser import (urlmarks, browsertab, inspector, navigate,
webelem, downloads) webelem, downloads)
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
objreg, utils, typing, debug) objreg, utils, debug)
from qutebrowser.utils.usertypes import KeyMode from qutebrowser.utils.usertypes import KeyMode
from qutebrowser.misc import editor, guiprocess from qutebrowser.misc import editor, guiprocess
from qutebrowser.completion.models import urlmodel, miscmodels from qutebrowser.completion.models import urlmodel, miscmodels
from qutebrowser.mainwindow import mainwindow
class CommandDispatcher: class CommandDispatcher:
@ -70,7 +72,6 @@ class CommandDispatcher:
def _new_tabbed_browser(self, private): def _new_tabbed_browser(self, private):
"""Get a tabbed-browser from a new window.""" """Get a tabbed-browser from a new window."""
from qutebrowser.mainwindow import mainwindow
new_window = mainwindow.MainWindow(private=private) new_window = mainwindow.MainWindow(private=private)
new_window.show() new_window.show()
return new_window.tabbed_browser return new_window.tabbed_browser

View File

@ -24,6 +24,7 @@ import posixpath
from qutebrowser.browser import webelem from qutebrowser.browser import webelem
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import objreg, urlutils, log, message, qtutils from qutebrowser.utils import objreg, urlutils, log, message, qtutils
from qutebrowser.mainwindow import mainwindow
class Error(Exception): class Error(Exception):
@ -134,7 +135,6 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
window=win_id) window=win_id)
if window: if window:
from qutebrowser.mainwindow import mainwindow
new_window = mainwindow.MainWindow( new_window = mainwindow.MainWindow(
private=cur_tabbed_browser.private) private=cur_tabbed_browser.private)
new_window.show() new_window.show()

View File

@ -23,6 +23,7 @@ import html
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import usertypes, message, log, objreg, jinja from qutebrowser.utils import usertypes, message, log, objreg, jinja
from qutebrowser.mainwindow import mainwindow
class CallSuper(Exception): class CallSuper(Exception):
@ -234,7 +235,6 @@ def get_tab(win_id, target):
elif target == usertypes.ClickTarget.window: elif target == usertypes.ClickTarget.window:
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id) window=win_id)
from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow(private=tabbed_browser.private) window = mainwindow.MainWindow(private=tabbed_browser.private)
window.show() window.show()
win_id = window.win_id win_id = window.win_id

View File

@ -31,6 +31,7 @@ from PyQt5.QtGui import QMouseEvent
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.mainwindow import mainwindow
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg from qutebrowser.utils import log, usertypes, utils, qtutils, objreg
@ -372,7 +373,6 @@ class AbstractWebElement(collections.abc.MutableMapping):
background = click_target == usertypes.ClickTarget.tab_bg background = click_target == usertypes.ClickTarget.tab_bg
tabbed_browser.tabopen(url, background=background) tabbed_browser.tabopen(url, background=background)
elif click_target == usertypes.ClickTarget.window: elif click_target == usertypes.ClickTarget.window:
from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow(private=tabbed_browser.private) window = mainwindow.MainWindow(private=tabbed_browser.private)
window.show() window.show()
window.tabbed_browser.tabopen(url) window.tabbed_browser.tabopen(url)

View File

@ -22,10 +22,11 @@
import inspect import inspect
import collections import collections
import traceback import traceback
import typing
from qutebrowser.commands import cmdexc, argparser from qutebrowser.commands import cmdexc, argparser
from qutebrowser.utils import (log, utils, message, docutils, objreg, from qutebrowser.utils import (log, utils, message, docutils, objreg,
usertypes, typing) usertypes)
from qutebrowser.utils import debug as debug_utils from qutebrowser.utils import debug as debug_utils
from qutebrowser.misc import objects from qutebrowser.misc import objects
@ -415,10 +416,7 @@ class Command:
# We also can't use isinstance here because typing.Union doesn't # We also can't use isinstance here because typing.Union doesn't
# support that. # support that.
# pylint: disable=no-member,useless-suppression # pylint: disable=no-member,useless-suppression
try: types = list(typ.__args__)
types = list(typ.__union_params__)
except AttributeError:
types = list(typ.__args__)
# pylint: enable=no-member,useless-suppression # pylint: enable=no-member,useless-suppression
if param.default is not inspect.Parameter.empty: if param.default is not inspect.Parameter.empty:
types.append(type(param.default)) types.append(type(param.default))

View File

@ -28,9 +28,9 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from qutebrowser.config import configdata, configexc, configtypes, configfiles from qutebrowser.config import configdata, configexc, configtypes, configfiles
from qutebrowser.utils import utils, objreg, message, log, usertypes from qutebrowser.utils import utils, objreg, message, log, usertypes, jinja
from qutebrowser.misc import objects, msgbox from qutebrowser.misc import objects, msgbox
from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.commands import cmdexc, cmdutils, runners
from qutebrowser.completion.models import configmodel from qutebrowser.completion.models import configmodel
# An easy way to access the config from other code via config.val.foo # An easy way to access the config from other code via config.val.foo
@ -176,8 +176,6 @@ class KeyConfig:
def bind(self, key, command, *, mode, force=False, save_yaml=False): def bind(self, key, command, *, mode, force=False, save_yaml=False):
"""Add a new binding from key to command.""" """Add a new binding from key to command."""
# Doing this here to work around a Python 3.4 circular import
from qutebrowser.commands import runners
key = self._prepare(key, mode) key = self._prepare(key, mode)
parser = runners.CommandParser() parser = runners.CommandParser()
@ -594,8 +592,6 @@ def set_register_stylesheet(obj, *, stylesheet=None, update=True):
@functools.lru_cache() @functools.lru_cache()
def _render_stylesheet(stylesheet): def _render_stylesheet(stylesheet):
"""Render the given stylesheet jinja template.""" """Render the given stylesheet jinja template."""
# Imported here to avoid a Python 3.4 circular import
from qutebrowser.utils import jinja
with jinja.environment.no_autoescape(): with jinja.environment.no_autoescape():
template = jinja.environment.from_string(stylesheet) template = jinja.environment.from_string(stylesheet)
return template.render(conf=val) return template.render(conf=val)

View File

@ -19,7 +19,7 @@
"""Exceptions related to config parsing.""" """Exceptions related to config parsing."""
from qutebrowser.utils import utils from qutebrowser.utils import utils, jinja
class Error(Exception): class Error(Exception):
@ -108,7 +108,6 @@ class ConfigFileErrors(Error):
def to_html(self): def to_html(self):
"""Get the error texts as a HTML snippet.""" """Get the error texts as a HTML snippet."""
from qutebrowser.utils import jinja
template = jinja.environment.from_string(""" template = jinja.environment.from_string("""
Errors occurred while reading {{ basename }}: Errors occurred while reading {{ basename }}:

View File

@ -30,7 +30,7 @@ import yaml
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
import qutebrowser import qutebrowser
from qutebrowser.config import configexc from qutebrowser.config import configexc, config
from qutebrowser.utils import standarddir, utils, qtutils from qutebrowser.utils import standarddir, utils, qtutils
@ -152,8 +152,8 @@ class ConfigAPI:
errors: Errors which occurred while setting options. errors: Errors which occurred while setting options.
""" """
def __init__(self, config, keyconfig): def __init__(self, conf, keyconfig):
self._config = config self._config = conf
self._keyconfig = keyconfig self._keyconfig = keyconfig
self.load_autoconfig = True self.load_autoconfig = True
self.errors = [] self.errors = []
@ -189,7 +189,6 @@ class ConfigAPI:
def read_config_py(filename=None): def read_config_py(filename=None):
"""Read a config.py file.""" """Read a config.py file."""
from qutebrowser.config import config
api = ConfigAPI(config.instance, config.key_instance) api = ConfigAPI(config.instance, config.key_instance)
if filename is None: if filename is None:

View File

@ -59,9 +59,9 @@ from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QColor, QFont from PyQt5.QtGui import QColor, QFont
from PyQt5.QtWidgets import QTabWidget, QTabBar from PyQt5.QtWidgets import QTabWidget, QTabBar
from qutebrowser.commands import cmdutils from qutebrowser.commands import cmdutils, runners, cmdexc
from qutebrowser.config import configexc from qutebrowser.config import configexc
from qutebrowser.utils import standarddir, utils, qtutils from qutebrowser.utils import standarddir, utils, qtutils, urlutils
SYSTEM_PROXY = object() # Return value for Proxy type SYSTEM_PROXY = object() # Return value for Proxy type
@ -791,7 +791,6 @@ class Command(BaseType):
if not Command.unvalidated: if not Command.unvalidated:
Command.unvalidated = True Command.unvalidated = True
try: try:
from qutebrowser.commands import runners, cmdexc
parser = runners.CommandParser() parser = runners.CommandParser()
try: try:
parser.parse_all(value) parser.parse_all(value)
@ -1287,7 +1286,6 @@ class Proxy(BaseType):
('none', "Don't use any proxy")) ('none', "Don't use any proxy"))
def to_py(self, value): def to_py(self, value):
from qutebrowser.utils import urlutils
self._basic_py_validation(value, str) self._basic_py_validation(value, str)
if not value: if not value:
return None return None
@ -1352,7 +1350,6 @@ class FuzzyUrl(BaseType):
"""A URL which gets interpreted as search if needed.""" """A URL which gets interpreted as search if needed."""
def to_py(self, value): def to_py(self, value):
from qutebrowser.utils import urlutils
self._basic_py_validation(value, str) self._basic_py_validation(value, str)
if not value: if not value:
return None return None

View File

@ -31,8 +31,7 @@ from qutebrowser.commands import runners, cmdutils
from qutebrowser.config import config, configfiles from qutebrowser.config import config, configfiles
from qutebrowser.utils import (message, log, usertypes, qtutils, objreg, utils, from qutebrowser.utils import (message, log, usertypes, qtutils, objreg, utils,
jinja, debug) jinja, debug)
from qutebrowser.mainwindow import tabbedbrowser, messageview, prompt from qutebrowser.mainwindow import messageview, prompt
from qutebrowser.mainwindow.statusbar import bar
from qutebrowser.completion import completionwidget, completer from qutebrowser.completion import completionwidget, completer
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.browser import (commands, downloadview, hints, from qutebrowser.browser import (commands, downloadview, hints,
@ -140,6 +139,11 @@ class MainWindow(QWidget):
parent: The parent the window should get. parent: The parent the window should get.
""" """
super().__init__(parent) super().__init__(parent)
# Late import to avoid a circular dependency
# - browsertab -> hints -> webelem -> mainwindow -> bar -> browsertab
from qutebrowser.mainwindow import tabbedbrowser
from qutebrowser.mainwindow.statusbar import bar
self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_DeleteOnClose)
self._commandrunner = None self._commandrunner = None
self._overlays = [] self._overlays = []

View File

@ -28,7 +28,7 @@ from PyQt5.QtGui import QIcon
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.mainwindow import tabwidget from qutebrowser.mainwindow import tabwidget, mainwindow
from qutebrowser.browser import signalfilter, browsertab from qutebrowser.browser import signalfilter, browsertab
from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg,
urlutils, message, jinja) urlutils, message, jinja)
@ -432,7 +432,6 @@ class TabbedBrowser(tabwidget.TabWidget):
if (config.val.tabs.tabs_are_windows and self.count() > 0 and if (config.val.tabs.tabs_are_windows and self.count() > 0 and
not ignore_tabs_are_windows): not ignore_tabs_are_windows):
from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow(private=self.private) window = mainwindow.MainWindow(private=self.private)
window.show() window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',

View File

@ -43,12 +43,12 @@ except ImportError: # pragma: no cover
# to stderr. # to stderr.
def check_python_version(): def check_python_version():
"""Check if correct python version is run.""" """Check if correct python version is run."""
if sys.hexversion < 0x03040000: if sys.hexversion < 0x03050000:
# We don't use .format() and print_function here just in case someone # We don't use .format() and print_function here just in case someone
# still has < 2.6 installed. # still has < 2.6 installed.
# pylint: disable=bad-builtin # pylint: disable=bad-builtin
version_str = '.'.join(map(str, sys.version_info[:3])) version_str = '.'.join(map(str, sys.version_info[:3]))
text = ("At least Python 3.4 is required to run qutebrowser, but " + text = ("At least Python 3.5 is required to run qutebrowser, but " +
version_str + " is installed!\n") version_str + " is installed!\n")
if Tk and '--no-err-windows' not in sys.argv: # pragma: no cover if Tk and '--no-err-windows' not in sys.argv: # pragma: no cover
root = Tk() root = Tk()

View File

@ -19,7 +19,7 @@
"""Things which need to be done really early (e.g. before importing Qt). """Things which need to be done really early (e.g. before importing Qt).
At this point we can be sure we have all python 3.4 features available. At this point we can be sure we have all python 3.5 features available.
""" """
try: try:

View File

@ -32,6 +32,7 @@ from qutebrowser.utils import (standarddir, objreg, qtutils, log, message,
from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config, configfiles from qutebrowser.config import config, configfiles
from qutebrowser.completion.models import miscmodels from qutebrowser.completion.models import miscmodels
from qutebrowser.mainwindow import mainwindow
default = object() # Sentinel value default = object() # Sentinel value
@ -371,7 +372,6 @@ class SessionManager(QObject):
name: The name of the session to load. name: The name of the session to load.
temp: If given, don't set the current session. temp: If given, don't set the current session.
""" """
from qutebrowser.mainwindow import mainwindow
path = self._get_session_path(name, check_exists=True) path = self._get_session_path(name, check_exists=True)
try: try:
with open(path, encoding='utf-8') as f: with open(path, encoding='utf-8') as f:

View File

@ -1,68 +0,0 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
# pylint: disable=unused-import,bad-mcs-method-argument
"""Wrapper for Python 3.5's typing module.
This wrapper is needed as both Python 3.5 and typing for PyPI isn't commonly
packaged yet. As we don't actually need anything from the typing module at
runtime, we instead mock the typing classes (using objects to make things
easier) so the typing module isn't a hard dependency.
"""
# Those are defined here to make them testable easily
class FakeTypingMeta(type):
"""Fake typing metaclass like typing.TypingMeta."""
def __init__(self, *args, # pylint: disable=super-init-not-called
**_kwds):
pass
class FakeUnionMeta(FakeTypingMeta):
"""Fake union metaclass metaclass like typing.UnionMeta."""
def __new__(cls, name, bases, namespace, parameters=None):
if parameters is None:
return super().__new__(cls, name, bases, namespace)
self = super().__new__(cls, name, bases, {})
self.__union_params__ = tuple(parameters)
return self
def __getitem__(self, parameters):
return self.__class__(self.__name__, self.__bases__,
dict(self.__dict__), parameters=parameters)
class FakeUnion(metaclass=FakeUnionMeta):
"""Fake Union type like typing.Union."""
__union_params__ = None
try:
from typing import Union
except ImportError: # pragma: no cover
Union = FakeUnion

View File

@ -42,7 +42,7 @@ except ImportError: # pragma: no cover
from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper
import qutebrowser import qutebrowser
from qutebrowser.utils import qtutils, log from qutebrowser.utils import qtutils, log, debug
fake_clipboard = None fake_clipboard = None
@ -425,14 +425,13 @@ class KeyInfo:
self.text = text self.text = text
def __repr__(self): def __repr__(self):
# Meh, dependency cycle...
from qutebrowser.utils.debug import qenum_key
if self.modifiers is None: if self.modifiers is None:
modifiers = None modifiers = None
else: else:
#modifiers = qflags_key(Qt, self.modifiers) #modifiers = qflags_key(Qt, self.modifiers)
modifiers = hex(int(self.modifiers)) modifiers = hex(int(self.modifiers))
return get_repr(self, constructor=True, key=qenum_key(Qt, self.key), return get_repr(self, constructor=True,
key=debug.qenum_key(Qt, self.key),
modifiers=modifiers, text=self.text) modifiers=modifiers, text=self.text)
def __eq__(self, other): def __eq__(self, other):
@ -814,10 +813,12 @@ def open_file(filename, cmdline=None):
the filename is appended to the cmdline. the filename is appended to the cmdline.
""" """
# Import late to avoid circular imports: # Import late to avoid circular imports:
# utils -> config -> configdata -> configtypes -> cmdutils -> command -> # - usertypes -> utils -> guiprocess -> message -> usertypes
# utils # - usertypes -> utils -> config -> configdata -> configtypes ->
from qutebrowser.misc import guiprocess # cmdutils -> command -> message -> usertypes
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.misc import guiprocess
# the default program to open downloads with - will be empty string # the default program to open downloads with - will be empty string
# if we want to use the default # if we want to use the default
override = config.val.downloads.open_dispatcher override = config.val.downloads.open_dispatcher

View File

@ -149,8 +149,6 @@ PERFECT_FILES = [
'utils/jinja.py'), 'utils/jinja.py'),
('tests/unit/utils/test_error.py', ('tests/unit/utils/test_error.py',
'utils/error.py'), 'utils/error.py'),
('tests/unit/utils/test_typing.py',
'utils/typing.py'),
('tests/unit/utils/test_javascript.py', ('tests/unit/utils/test_javascript.py',
'utils/javascript.py'), 'utils/javascript.py'),
@ -291,7 +289,7 @@ def main_check_all():
tests. tests.
This runs pytest with the used executable, so check_coverage.py should be This runs pytest with the used executable, so check_coverage.py should be
called with something like ./.tox/py34/bin/python. called with something like ./.tox/py36/bin/python.
""" """
for test_file, src_file in PERFECT_FILES: for test_file, src_file in PERFECT_FILES:
if test_file is None: if test_file is None:

View File

@ -5,10 +5,6 @@
# #
case $TESTENV in case $TESTENV in
py34-cov)
exe=/usr/bin/python3.4
full=full
;;
py3*-pyqt*) py3*-pyqt*)
exe=$(readlink -f .tox/$TESTENV/bin/python) exe=$(readlink -f .tox/$TESTENV/bin/python)
full= full=

View File

@ -121,7 +121,8 @@ setupdata = {
'Operating System :: Microsoft :: Windows :: Windows 7', 'Operating System :: Microsoft :: Windows :: Windows 7',
'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet', 'Topic :: Internet',
'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Browsers', 'Topic :: Internet :: WWW/HTTP :: Browsers',

View File

@ -265,7 +265,7 @@ def test_launching_with_python2():
pytest.skip("python2 not found") pytest.skip("python2 not found")
_stdout, stderr = proc.communicate() _stdout, stderr = proc.communicate()
assert proc.returncode == 1 assert proc.returncode == 1
error = "At least Python 3.4 is required to run qutebrowser" error = "At least Python 3.5 is required to run qutebrowser"
assert stderr.decode('ascii').startswith(error) assert stderr.decode('ascii').startswith(error)

View File

@ -24,11 +24,12 @@
import sys import sys
import logging import logging
import types import types
import typing
import pytest import pytest
from qutebrowser.commands import cmdutils, cmdexc, argparser, command from qutebrowser.commands import cmdutils, cmdexc, argparser, command
from qutebrowser.utils import usertypes, typing from qutebrowser.utils import usertypes
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

View File

@ -28,7 +28,7 @@ import pytest
from qutebrowser.misc import checkpyver from qutebrowser.misc import checkpyver
TEXT = (r"At least Python 3.4 is required to run qutebrowser, but " TEXT = (r"At least Python 3.5 is required to run qutebrowser, but "
r"\d+\.\d+\.\d+ is installed!\n") r"\d+\.\d+\.\d+ is installed!\n")
@ -60,7 +60,7 @@ def test_patched_no_errwindow(capfd, monkeypatch):
"""Test with a patched sys.hexversion and --no-err-windows.""" """Test with a patched sys.hexversion and --no-err-windows."""
monkeypatch.setattr(checkpyver.sys, 'argv', monkeypatch.setattr(checkpyver.sys, 'argv',
[sys.argv[0], '--no-err-windows']) [sys.argv[0], '--no-err-windows'])
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03000000) monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03040000)
monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None) monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None)
checkpyver.check_python_version() checkpyver.check_python_version()
stdout, stderr = capfd.readouterr() stdout, stderr = capfd.readouterr()
@ -70,7 +70,7 @@ def test_patched_no_errwindow(capfd, monkeypatch):
def test_patched_errwindow(capfd, mocker, monkeypatch): def test_patched_errwindow(capfd, mocker, monkeypatch):
"""Test with a patched sys.hexversion and a fake Tk.""" """Test with a patched sys.hexversion and a fake Tk."""
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03000000) monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03040000)
monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None) monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None)
try: try:

View File

@ -1,54 +0,0 @@
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Tests for qutebrowser.utils.typing."""
import pytest
from qutebrowser.utils import typing
@pytest.fixture
def pytyping():
"""A fixture to get the python 3.5+ typing module."""
pytyping = pytest.importorskip('typing')
return pytyping
class TestUnion:
def test_python_subclass(self, pytyping):
assert (type(pytyping.Union[str, int]) is # flake8: disable=E721
type(pytyping.Union))
def test_qute_subclass(self):
assert (type(typing.FakeUnion[str, int]) is # flake8: disable=E721
type(typing.FakeUnion))
def test_python_params(self, pytyping):
union = pytyping.Union[str, int]
try:
assert union.__union_params__ == (str, int)
except AttributeError:
assert union.__args__ == (str, int)
def test_qute_params(self):
union = typing.FakeUnion[str, int]
try:
assert union.__union_params__ == (str, int)
except AttributeError:
assert union.__args__ == (str, int)