Switch to python 3.4 enums.
Our home-brewn enum wasn't really liked by pylint (many no-member errors), so instead of adding some workaround, we just use the python 3.4 enum instead. This however also means we need to depend on Python 3.4 and not 3.3. Maybe we should use enum34 on Python < 3.3.
This commit is contained in:
parent
54c7f29f04
commit
8d80ce2628
@ -69,7 +69,7 @@ Requirements
|
||||
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* http://www.python.org/[Python] 3.3 or newer (3.4 recommended)
|
||||
* http://www.python.org/[Python] 3.4
|
||||
* http://qt-project.org/[Qt] 5.2 or newer (5.3.1 recommended)
|
||||
* QtWebKit
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2 or newer
|
||||
|
@ -8,7 +8,7 @@ pkgdesc="A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit.",
|
||||
arch=(any)
|
||||
url="http://www.qutebrowser.org/"
|
||||
license=('GPL')
|
||||
depends=('python>=3.3' 'python-setuptools' 'python-pyqt5>=5.2' 'qt5-base>=5.2'
|
||||
depends=('python>=3.4' 'python-setuptools' 'python-pyqt5>=5.2' 'qt5-base>=5.2'
|
||||
'qt5-webkit>=5.2' 'libxkbcommon-x11' 'python-rfc6266')
|
||||
makedepends=('python' 'python-setuptools')
|
||||
optdepends=('python-colorlog: colored logging output')
|
||||
|
@ -39,8 +39,8 @@ from qutebrowser.utils.qt import qt_ensure_valid
|
||||
ElemTuple = namedtuple('ElemTuple', 'elem, label')
|
||||
|
||||
|
||||
Target = enum('normal', 'tab', 'tab_bg', 'yank', 'yank_primary', 'cmd',
|
||||
'cmd_tab', 'cmd_tab_bg', 'rapid', 'download')
|
||||
Target = enum('Target', 'normal', 'tab', 'tab_bg', 'yank', 'yank_primary',
|
||||
'cmd', 'cmd_tab', 'cmd_tab_bg', 'rapid', 'download')
|
||||
|
||||
|
||||
class HintContext:
|
||||
@ -280,7 +280,7 @@ class HintManager(QObject):
|
||||
target = Target.tab_bg
|
||||
else:
|
||||
target = self._context.target
|
||||
self.set_open_target.emit(Target[target])
|
||||
self.set_open_target.emit(target.name)
|
||||
# FIXME Instead of clicking the center, we could have nicer heuristics.
|
||||
# e.g. parse (-webkit-)border-radius correctly and click text fields at
|
||||
# the bottom right, and everything else on the top left or so.
|
||||
|
@ -34,7 +34,7 @@ import qutebrowser.config.config as config
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
from qutebrowser.utils.misc import get_standard_dir
|
||||
|
||||
MapType = enum('attribute', 'setter', 'static_setter')
|
||||
MapType = enum('MapType', 'attribute', 'setter', 'static_setter')
|
||||
|
||||
|
||||
MAPPINGS = {
|
||||
|
@ -71,8 +71,8 @@ class BaseKeyParser(QObject):
|
||||
keystring_updated = pyqtSignal(str)
|
||||
do_log = True
|
||||
|
||||
Match = enum('partial', 'definitive', 'ambiguous', 'none')
|
||||
Type = enum('chain', 'special')
|
||||
Match = enum('Match', 'partial', 'definitive', 'ambiguous', 'none')
|
||||
Type = enum('Type', 'chain', 'special')
|
||||
|
||||
def __init__(self, parent=None, supports_count=None,
|
||||
supports_chains=False):
|
||||
|
@ -33,7 +33,7 @@ from qutebrowser.utils.log import keyboard as logger
|
||||
|
||||
|
||||
STARTCHARS = ":/?"
|
||||
LastPress = enum('none', 'filtertext', 'keystring')
|
||||
LastPress = enum('LastPress', 'none', 'filtertext', 'keystring')
|
||||
|
||||
|
||||
class NormalKeyParser(CommandKeyParser):
|
||||
@ -120,8 +120,8 @@ class HintKeyParser(CommandKeyParser):
|
||||
e.key(), e.text()))
|
||||
if e.key() == Qt.Key_Backspace:
|
||||
logger.debug("Got backspace, mode {}, filtertext '{}', keystring "
|
||||
"'{}'".format(LastPress[self._last_press],
|
||||
self._filtertext, self._keystring))
|
||||
"'{}'".format(self._last_press, self._filtertext,
|
||||
self._keystring))
|
||||
if self._last_press == LastPress.filtertext and self._filtertext:
|
||||
self._filtertext = self._filtertext[:-1]
|
||||
self.filter_hints.emit(self._filtertext)
|
||||
|
@ -30,7 +30,7 @@ from qutebrowser.utils.usertypes import enum
|
||||
from qutebrowser.utils.qt import qt_ensure_valid
|
||||
|
||||
|
||||
Role = enum('marks', 'sort', start=Qt.UserRole)
|
||||
Role = enum('Role', 'marks', 'sort', start=Qt.UserRole, is_int=True)
|
||||
|
||||
|
||||
class BaseCompletionModel(QStandardItemModel):
|
||||
|
@ -28,7 +28,7 @@ from qutebrowser.utils.usertypes import enum
|
||||
from qutebrowser.utils.qt import qt_ensure_valid
|
||||
|
||||
|
||||
Role = enum('item', start=Qt.UserRole)
|
||||
Role = enum('Role', 'item', start=Qt.UserRole, is_int=True)
|
||||
|
||||
|
||||
class DownloadModel(QAbstractListModel):
|
||||
|
@ -23,6 +23,8 @@ import unittest
|
||||
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
# FIXME: Add some more tests, e.g. for is_int
|
||||
|
||||
|
||||
class EnumTests(unittest.TestCase):
|
||||
|
||||
@ -33,33 +35,28 @@ class EnumTests(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.enum = enum('zero', 'one')
|
||||
self.enum = enum('Enum', 'one', 'two')
|
||||
|
||||
def test_values(self):
|
||||
"""Test if enum members resolve to the right values."""
|
||||
self.assertEqual(self.enum.zero, 0)
|
||||
self.assertEqual(self.enum.one, 1)
|
||||
self.assertEqual(self.enum.one.value, 1)
|
||||
self.assertEqual(self.enum.two.value, 2)
|
||||
|
||||
def test_reverse(self):
|
||||
"""Test reverse mapping."""
|
||||
self.assertEqual(self.enum[0], 'zero')
|
||||
self.assertEqual(self.enum[1], 'one')
|
||||
def test_name(self):
|
||||
"""Test .name mapping."""
|
||||
self.assertEqual(self.enum.one.name, 'one')
|
||||
self.assertEqual(self.enum.two.name, 'two')
|
||||
|
||||
def test_unknown(self):
|
||||
"""Test invalid values which should raise an AttributeError."""
|
||||
with self.assertRaises(AttributeError):
|
||||
_ = self.enum.two
|
||||
|
||||
def test_unknown_reverse(self):
|
||||
"""Test reverse mapping with invalid value ."""
|
||||
with self.assertRaises(KeyError):
|
||||
_ = self.enum['two']
|
||||
_ = self.enum.three
|
||||
|
||||
def test_start(self):
|
||||
"""Test the start= argument."""
|
||||
e = enum('three', 'four', start=3)
|
||||
self.assertEqual(e.three, 3)
|
||||
self.assertEqual(e.four, 4)
|
||||
e = enum('Enum', 'three', 'four', start=3)
|
||||
self.assertEqual(e.three.value, 3)
|
||||
self.assertEqual(e.four.value, 4)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -40,11 +40,11 @@ except ImportError:
|
||||
# to stderr.
|
||||
def check_python_version():
|
||||
"""Check if correct python version is run."""
|
||||
if sys.hexversion < 0x03030000:
|
||||
if sys.hexversion < 0x03040000:
|
||||
# We don't use .format() and print_function here just in case someone
|
||||
# still has < 2.6 installed.
|
||||
version_str = '.'.join(map(str, sys.version_info[:3]))
|
||||
text = ("At least Python 3.3 is required to run qutebrowser, but " +
|
||||
text = ("At least Python 3.4 is required to run qutebrowser, but " +
|
||||
version_str + " is installed!\n")
|
||||
if Tk:
|
||||
root = Tk()
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
"""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.3 features available.
|
||||
At this point we can be sure we have all python 3.4 features available.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -25,6 +25,7 @@ Module attributes:
|
||||
|
||||
import operator
|
||||
import collections.abc
|
||||
import enum as pyenum
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QObject, QTimer
|
||||
|
||||
@ -34,21 +35,20 @@ from qutebrowser.utils.log import misc as logger
|
||||
_UNSET = object()
|
||||
|
||||
|
||||
def enum(*items, start=0):
|
||||
def enum(name, *items, start=1, is_int=False):
|
||||
"""Factory for simple enumerations.
|
||||
|
||||
We really don't need more complex things here, so we don't use python3.4's
|
||||
enum, because we'd have to backport things to 3.3.
|
||||
|
||||
Based on: http://stackoverflow.com/a/1695250/2085149
|
||||
|
||||
Args:
|
||||
name: Name of the enum
|
||||
*items: Items to be sequentally enumerated.
|
||||
start: The number to use for the first value.
|
||||
We use 1 as default so enum members are always True.
|
||||
is_init: True if the enum should be a Python IntEnum
|
||||
"""
|
||||
numbers = range(start, len(items) + start)
|
||||
enums = dict(zip(items, numbers))
|
||||
return EnumBase('Enum', (), enums)
|
||||
enums = [(v, i) for (i, v) in enumerate(items, start)]
|
||||
base = pyenum.IntEnum if is_int else pyenum.Enum
|
||||
base = pyenum.unique(base)
|
||||
return base(name, enums)
|
||||
|
||||
|
||||
class EnumBase(type):
|
||||
@ -77,7 +77,7 @@ class NeighborList(collections.abc.Sequence):
|
||||
_mode: The current mode.
|
||||
"""
|
||||
|
||||
Modes = enum('block', 'wrap', 'exception')
|
||||
Modes = enum('Modes', 'block', 'wrap', 'exception')
|
||||
|
||||
def __init__(self, items=None, default=_UNSET, mode=Modes.exception):
|
||||
"""Constructor.
|
||||
@ -233,10 +233,10 @@ class NeighborList(collections.abc.Sequence):
|
||||
|
||||
|
||||
# The mode of a Question.
|
||||
PromptMode = enum('yesno', 'text', 'user_pwd', 'alert')
|
||||
PromptMode = enum('PromptMode', 'yesno', 'text', 'user_pwd', 'alert')
|
||||
|
||||
# Where to open a clicked link.
|
||||
ClickTarget = enum('normal', 'tab', 'tab_bg')
|
||||
ClickTarget = enum('ClickTarget', 'normal', 'tab', 'tab_bg')
|
||||
|
||||
|
||||
class Question(QObject):
|
||||
|
@ -36,7 +36,8 @@ from qutebrowser.utils.usertypes import enum
|
||||
from qutebrowser.utils.misc import compact_text
|
||||
|
||||
|
||||
Group = enum('all', 'links', 'images', 'editable', 'url', 'prevnext', 'focus')
|
||||
Group = enum('Group', 'all', 'links', 'images', 'editable', 'url', 'prevnext',
|
||||
'focus')
|
||||
|
||||
|
||||
SELECTORS = {
|
||||
|
@ -24,6 +24,11 @@ from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt
|
||||
from qutebrowser.widgets.webview import LoadStatus
|
||||
from qutebrowser.widgets.statusbar.textbase import TextBase
|
||||
from qutebrowser.config.style import set_register_stylesheet, get_stylesheet
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
|
||||
# Note this has entries for success/error/warn from widgets.webview:LoadStatus
|
||||
UrlType = enum('UrlType', 'success', 'error', 'warn', 'hover', 'normal')
|
||||
|
||||
|
||||
class UrlText(TextBase):
|
||||
@ -34,8 +39,8 @@ class UrlText(TextBase):
|
||||
STYLESHEET: The stylesheet template.
|
||||
|
||||
Attributes:
|
||||
normal_url: The normal URL to be displayed.
|
||||
normal_url_type: The type of the normal URL.
|
||||
normal_url: The normal URL to be displayed as a UrlType instance.
|
||||
normal_url_type: The type of the normal URL as a UrlType instance.
|
||||
hover_url: The URL we're currently hovering over.
|
||||
_ssl_errors: Whether SSL errors occured while loading.
|
||||
|
||||
@ -78,13 +83,20 @@ class UrlText(TextBase):
|
||||
set_register_stylesheet(self)
|
||||
self._hover_url = None
|
||||
self._normal_url = None
|
||||
self._normal_url_type = 'normal'
|
||||
self._normal_url_type = UrlType.normal
|
||||
|
||||
@pyqtProperty(str)
|
||||
def urltype(self):
|
||||
"""Getter for self.urltype, so it can be used as Qt property."""
|
||||
"""Getter for self.urltype, so it can be used as Qt property.
|
||||
|
||||
Return:
|
||||
The urltype as a string (!)
|
||||
"""
|
||||
# pylint: disable=method-hidden
|
||||
return self._urltype
|
||||
if self._urltype is None:
|
||||
return ""
|
||||
else:
|
||||
return self._urltype.name
|
||||
|
||||
@urltype.setter
|
||||
def urltype(self, val):
|
||||
@ -121,7 +133,11 @@ class UrlText(TextBase):
|
||||
|
||||
@normal_url_type.setter
|
||||
def normal_url_type(self, val):
|
||||
"""Setter to update displayed URL when normal_url_type was set."""
|
||||
"""Setter to update displayed URL when normal_url_type was set.
|
||||
|
||||
Args:
|
||||
val: The value as an UrlType instance.
|
||||
"""
|
||||
self._normal_url_type = val
|
||||
self._update_url()
|
||||
|
||||
@ -129,25 +145,26 @@ class UrlText(TextBase):
|
||||
"""Update the displayed URL if the url or the hover url changed."""
|
||||
if self.hover_url is not None:
|
||||
self.setText(self.hover_url)
|
||||
self.urltype = 'hover'
|
||||
self.urltype = UrlType.hover
|
||||
elif self.normal_url is not None:
|
||||
self.setText(self.normal_url)
|
||||
self.urltype = self.normal_url_type
|
||||
else:
|
||||
self.setText('')
|
||||
self.urltype = 'normal'
|
||||
self.urltype = UrlType.normal
|
||||
|
||||
@pyqtSlot(str)
|
||||
def on_load_status_changed(self, status):
|
||||
def on_load_status_changed(self, status_str):
|
||||
"""Slot for load_status_changed. Sets URL color accordingly.
|
||||
|
||||
Args:
|
||||
status: The LoadStatus as string.
|
||||
status_str: The LoadStatus as string.
|
||||
"""
|
||||
if status in ('success', 'error', 'warn'):
|
||||
self.normal_url_type = status
|
||||
status = LoadStatus[status_str]
|
||||
if status in (LoadStatus.success, LoadStatus.error, LoadStatus.warn):
|
||||
self.normal_url_type = UrlType[status_str]
|
||||
else:
|
||||
self.normal_url_type = 'normal'
|
||||
self.normal_url_type = UrlType.normal
|
||||
|
||||
@pyqtSlot(str)
|
||||
def set_url(self, s):
|
||||
@ -157,7 +174,7 @@ class UrlText(TextBase):
|
||||
s: The URL to set as string.
|
||||
"""
|
||||
self.normal_url = s
|
||||
self.normal_url_type = 'normal'
|
||||
self.normal_url_type = UrlType.normal
|
||||
|
||||
@pyqtSlot(str, str, str)
|
||||
def set_hover_url(self, link, _title, _text):
|
||||
@ -181,8 +198,4 @@ class UrlText(TextBase):
|
||||
"""Update URL if the tab changed."""
|
||||
self.hover_url = None
|
||||
self.normal_url = tab.url_text
|
||||
status = LoadStatus[tab.load_status]
|
||||
if status in ('success', 'error', 'warn'):
|
||||
self.normal_url_type = status
|
||||
else:
|
||||
self.normal_url_type = 'normal'
|
||||
self.on_load_status_changed(tab.load_status.name)
|
||||
|
@ -39,7 +39,7 @@ from qutebrowser.utils.usertypes import NeighborList, ClickTarget, enum
|
||||
from qutebrowser.commands.exceptions import CommandError
|
||||
|
||||
|
||||
LoadStatus = enum('none', 'success', 'error', 'warn', 'loading')
|
||||
LoadStatus = enum('LoadStatus', 'none', 'success', 'error', 'warn', 'loading')
|
||||
|
||||
|
||||
class WebView(QWebView):
|
||||
@ -138,10 +138,9 @@ class WebView(QWebView):
|
||||
Emit:
|
||||
load_status_changed
|
||||
"""
|
||||
log.webview.debug("load status for {}: {}".format(
|
||||
repr(self), LoadStatus[val]))
|
||||
log.webview.debug("load status for {}: {}".format(repr(self), val))
|
||||
self._load_status = val
|
||||
self.load_status_changed.emit(LoadStatus[val])
|
||||
self.load_status_changed.emit(val.name)
|
||||
|
||||
@property
|
||||
def url_text(self):
|
||||
@ -262,7 +261,7 @@ class WebView(QWebView):
|
||||
self.open_target = self._force_open_target
|
||||
self._force_open_target = None
|
||||
log.mouse.debug("Setting force target: {}".format(
|
||||
ClickTarget[self.open_target]))
|
||||
self.open_target))
|
||||
elif (e.button() == Qt.MidButton or
|
||||
e.modifiers() & Qt.ControlModifier):
|
||||
if config.get('general', 'background-tabs'):
|
||||
@ -270,7 +269,7 @@ class WebView(QWebView):
|
||||
else:
|
||||
self.open_target = ClickTarget.tab
|
||||
log.mouse.debug("Middle click, setting target: {}".format(
|
||||
ClickTarget[self.open_target]))
|
||||
self.open_target))
|
||||
else:
|
||||
self.open_target = ClickTarget.normal
|
||||
log.mouse.debug("Normal click, setting normal target")
|
||||
|
@ -54,8 +54,8 @@ def _parse_docstring(func): # noqa
|
||||
A (short_desc, long_desc, arg_descs) tuple.
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
State = enum('short', 'desc', # pylint: disable=invalid-name
|
||||
'desc_hidden', 'arg_start', 'arg_inside', 'misc')
|
||||
State = enum('State', 'short', 'desc', 'desc_hidden', 'arg_start',
|
||||
'arg_inside', 'misc')
|
||||
doc = inspect.getdoc(func)
|
||||
lines = doc.splitlines()
|
||||
|
||||
|
@ -111,7 +111,6 @@ setupdata = {
|
||||
'Operating System :: Microsoft :: Windows :: Windows 7',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Topic :: Internet',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
|
Loading…
Reference in New Issue
Block a user