From 2863eb7ca43300b137322e05d1674d6a0723b71b Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 17 Jan 2016 02:04:05 -0800 Subject: [PATCH] pyqt: update to the PyQt4 v2 API for QString and friends Eliminate all usage of QString and other remnants from the PyQt4 v1 API. Explicitly set the API to v2 in the new sipcompat module. Old-style signals and slots are still used. git-cola will switch to new-style signals and slots in v3.0. Related-to: #232 Signed-off-by: David Aguilar --- .travis.yml | 2 +- cola/actions.py | 3 +++ cola/app.py | 11 ++--------- cola/gravatar.py | 14 ++++++++------ cola/i18n.py | 23 +++++++++++++---------- cola/models/browse.py | 5 ++++- cola/qtcompat.py | 3 +++ cola/sipcompat.py | 30 ++++++++++++++++++++++++++++++ cola/widgets/completion.py | 7 +++++-- cola/widgets/dag.py | 2 +- cola/widgets/filetree.py | 5 ++--- cola/widgets/grep.py | 4 +--- cola/widgets/patch.py | 7 +++---- cola/widgets/prefs.py | 6 +++--- test/i18n_test.py | 21 ++------------------- 15 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 cola/sipcompat.py diff --git a/.travis.yml b/.travis.yml index 71607dfb10..4f42834e47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,6 @@ script: - git config --global user.name "Git Cola" - git config --global user.email git-cola@localhost.localdomain # TODO tests currently fail on Python3 due to sip - - env GIT_COLA_NO_SIP_SETAPI=1 GIT_COLA_NO_HISTORY=1 make test flags=--verbose + - env GIT_COLA_NO_HISTORY=1 make test flags=--verbose # pylint 1.4 uses python2.7+ syntax so skip pylint on python2.6 - test "2.6" = "$TRAVIS_PYTHON_VERSION" || make pylint diff --git a/cola/actions.py b/cola/actions.py index 25418a2a1d..e3c427606a 100644 --- a/cola/actions.py +++ b/cola/actions.py @@ -1,5 +1,8 @@ from __future__ import absolute_import +from cola import sipcompat +sipcompat.initialize() + from PyQt4.QtCore import SIGNAL from cola import cmds diff --git a/cola/app.py b/cola/app.py index 71fa23a6b5..8f10d8fd81 100644 --- a/cola/app.py +++ b/cola/app.py @@ -35,19 +35,12 @@ try: - import sip + from cola import sipcompat except ImportError: sys.stderr.write(errmsg) sys.exit(EX_UNAVAILABLE) -if not os.getenv('GIT_COLA_NO_SIP_SETAPI', False): - sip.setapi('QString', 1) - sip.setapi('QDate', 1) - sip.setapi('QDateTime', 1) - sip.setapi('QTextStream', 1) - sip.setapi('QTime', 1) - sip.setapi('QUrl', 1) - sip.setapi('QVariant', 1) +sipcompat.initialize() try: from PyQt4 import QtCore diff --git a/cola/gravatar.py b/cola/gravatar.py index 5ab976da3e..756663d967 100644 --- a/cola/gravatar.py +++ b/cola/gravatar.py @@ -2,6 +2,9 @@ import time +from cola import sipcompat +sipcompat.initialize() + from PyQt4 import QtGui from PyQt4 import QtCore from PyQt4 import QtNetwork @@ -19,8 +22,8 @@ class Gravatar(object): @staticmethod def url_for_email(email, imgsize): email_hash = hashlib.md5(core.encode(email)).hexdigest() - default_url = b'https://git-cola.github.io/images/git-64x64.jpg' - encoded_url = urllib.quote(default_url, b'') + default_url = 'https://git-cola.github.io/images/git-64x64.jpg' + encoded_url = urllib.quote(default_url, '') query = '?s=%d&d=%s' % (imgsize, encoded_url) url = 'https://gravatar.com/avatar/' + email_hash + query return url @@ -76,9 +79,8 @@ def network_finished(self, reply): header = QtCore.QByteArray('Location') raw_header = reply.rawHeader(header) if raw_header: - location = ustr(QtCore.QString(raw_header)).strip() - request_location = ustr( - Gravatar.url_for_email(self.email, self.imgsize)) + location = ustr(raw_header).strip() + request_location = Gravatar.url_for_email(self.email, self.imgsize) relocated = location != request_location else: relocated = False @@ -103,7 +105,7 @@ def network_finished(self, reply): # email address. We can't blindly trust self.email else # we may add cache entries for thee wrong email address. url = Gravatar.url_for_email(email, self.imgsize) - if url == ustr(reply.url().toString()): + if url == reply.url().toString(): self.pixmaps[email] = pixmap def set_pixmap_from_response(self): diff --git a/cola/i18n.py b/cola/i18n.py index dfebc7231e..9bfc8c019f 100644 --- a/cola/i18n.py +++ b/cola/i18n.py @@ -10,22 +10,29 @@ from cola import resources _null_translation = _gettext.NullTranslations() -# Python 3 compat -if not hasattr(_null_translation, 'ugettext'): - _null_translation.ugettext = _null_translation.gettext - _null_translation.ungettext = _null_translation.ngettext _translation = _null_translation def gettext(s): - txt = _translation.ugettext(s) + try: + txt = _translation.ugettext(s) + except AttributeError: + # Python 3 compat + _translation.ugettext = _translation.gettext + txt = _translation.gettext(s) if txt[-6:-4] == '@@': # handle @@verb / @@noun txt = txt[:-6] return txt def ngettext(s, p, n): - return _translation.ungettext(s, p, n) + try: + txt = _translation.ungettext(s, p, n) + except AttributeError: + # Python 3 compat + _translation.ungettext = _translation.ngettext + txt = _translation.ngettext(s, p, n) + return txt def N_(s): @@ -45,10 +52,6 @@ def install(locale): _translation = _gettext.translation('git-cola', localedir=_get_locale_dir(), fallback=True) - # Python 3 compat - if not hasattr(_translation, 'ugettext'): - _translation.ugettext = _translation.gettext - _translation.ungettext = _translation.ngettext def uninstall(): global _translation diff --git a/cola/models/browse.py b/cola/models/browse.py index 0566ab3ae7..c098ab7df0 100644 --- a/cola/models/browse.py +++ b/cola/models/browse.py @@ -3,6 +3,9 @@ import collections import time +from cola import sipcompat +sipcompat.initialize() + from PyQt4 import QtCore from PyQt4 import QtGui from PyQt4.QtCore import Qt @@ -233,7 +236,7 @@ def _initialize(self): self.setColumnCount(len(Columns.ALL)) for idx, header in enumerate(Columns.ALL): text = Columns.text(header) - self.setHeaderData(idx, Qt.Horizontal, QtCore.QVariant(text)) + self.setHeaderData(idx, Qt.Horizontal, text) self._entries = {} self._dir_rows = collections.defaultdict(int) diff --git a/cola/qtcompat.py b/cola/qtcompat.py index 2b85424580..e9d7cb671c 100644 --- a/cola/qtcompat.py +++ b/cola/qtcompat.py @@ -1,5 +1,8 @@ from __future__ import division, absolute_import, unicode_literals +from cola import sipcompat +sipcompat.initialize() + from PyQt4 import QtGui from PyQt4 import QtCore diff --git a/cola/sipcompat.py b/cola/sipcompat.py new file mode 100644 index 0000000000..db0e660db9 --- /dev/null +++ b/cola/sipcompat.py @@ -0,0 +1,30 @@ +from __future__ import absolute_import, division, unicode_literals + +import sip + + +class _PyQtSipApi(object): + + def __init__(self): + self._initialized = False + + def initialize(self): + if self._initialized: + return + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QString', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) + sip.setapi('QVariant', 2) + + self._initialized = True + +# It's a global, but in this case the API compat level truly +# is a global that can only be set once before PyQt4 is imported. +_pyqt_api = _PyQtSipApi() + + +def initialize(): + _pyqt_api.initialize() diff --git a/cola/widgets/completion.py b/cola/widgets/completion.py index 52cfe9a68a..f28654e23d 100644 --- a/cola/widgets/completion.py +++ b/cola/widgets/completion.py @@ -2,6 +2,9 @@ import re +from cola import sipcompat +sipcompat.initialize() + from PyQt4 import QtCore from PyQt4 import QtGui from PyQt4.QtCore import Qt @@ -286,7 +289,7 @@ def paint(self, painter, option, index): if not self.highlight_text: return QtGui.QStyledItemDelegate.paint(self, painter, option, index) - text = ustr(index.data().toPyObject()) + text = index.data() if self.case_sensitive: html = text.replace(self.highlight_text, '%s' % self.highlight_text) @@ -305,7 +308,7 @@ def paint(self, painter, option, index): # Painting item without text, Text Document will paint the text optionV4 = QtGui.QStyleOptionViewItemV4(option) self.initStyleOption(optionV4, index) - optionV4.text = QtCore.QString() + optionV4.text = '' style = QtGui.QApplication.style() style.drawControl(QtGui.QStyle.CE_ItemViewItem, optionV4, painter) diff --git a/cola/widgets/dag.py b/cola/widgets/dag.py index ef063b512c..27b24a4f12 100644 --- a/cola/widgets/dag.py +++ b/cola/widgets/dag.py @@ -912,7 +912,7 @@ def itemChange(self, change, value): self.scene().parent().set_selecting(False) # Cache the pen for use in paint() - if value.toPyObject(): + if value: self.brush = self.commit_selected_color color = self.selected_outline_color else: diff --git a/cola/widgets/filetree.py b/cola/widgets/filetree.py index 2f9d0e0488..2e3aa6135a 100644 --- a/cola/widgets/filetree.py +++ b/cola/widgets/filetree.py @@ -4,7 +4,6 @@ from PyQt4 import QtGui from cola import icons -from cola.compat import ustr from cola.widgets import standard @@ -26,14 +25,14 @@ def set_filenames(self, filenames, select=False): item = QtGui.QTreeWidgetItem() item.setIcon(0, icon) item.setText(0, filename) - item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(filename)) + item.setData(0, QtCore.Qt.UserRole, filename) items.append(item) self.addTopLevelItems(items) if select: self.setItemSelected(items[0], True) def filename_from_item(self, item): - return ustr(item.data(0, QtCore.Qt.UserRole).toPyObject()) + return item.data(0, QtCore.Qt.UserRole) def has_selection(self): return bool(self.selectedItems()) diff --git a/cola/widgets/grep.py b/cola/widgets/grep.py index c792f9d4d6..79bca0f768 100644 --- a/cola/widgets/grep.py +++ b/cola/widgets/grep.py @@ -10,7 +10,6 @@ from cola import utils from cola import qtutils from cola.cmds import do -from cola.compat import ustr from cola.git import git from cola.i18n import N_ from cola.qtutils import diff_font @@ -180,8 +179,7 @@ def done(self, exit_code): def regexp_mode(self): idx = self.regexp_combo.currentIndex() - data = self.regexp_combo.itemData(idx, Qt.UserRole).toPyObject() - return ustr(data) + return self.regexp_combo.itemData(idx, Qt.UserRole) def search(self): self.edit_group.setEnabled(False) diff --git a/cola/widgets/patch.py b/cola/widgets/patch.py index ab39b8bd47..f5700e5146 100644 --- a/cola/widgets/patch.py +++ b/cola/widgets/patch.py @@ -49,7 +49,7 @@ def get_patches_from_mimedata(mimedata): urls = mimedata.urls() if not urls: return [] - paths = [ustr(x.path()) for x in urls] + paths = [x.path() for x in urls] return get_patches_from_paths(paths) @@ -131,7 +131,7 @@ def apply_patches(self): items = self.tree.items() if not items: return - patches = [ustr(i.data(0, Qt.UserRole).toPyObject()) for i in items] + patches = [i.data(0, Qt.UserRole) for i in items] cmds.do(cmds.ApplyPatches, patches) self.accept() @@ -141,7 +141,6 @@ def add_files(self): filter='Patches (*.patch *.mbox)') if not files: return - files = [ustr(f) for f in files] self.curdir = os.path.dirname(files[0]) self.add_paths([os.path.relpath(f) for f in files]) @@ -181,7 +180,7 @@ def add_paths(self, paths): item.setFlags(flags) item.setIcon(0, icon) item.setText(0, os.path.basename(patch)) - item.setData(0, Qt.UserRole, QtCore.QVariant(patch)) + item.setData(0, Qt.UserRole, patch) item.setToolTip(0, patch) items.append(item) self.addTopLevelItems(items) diff --git a/cola/widgets/prefs.py b/cola/widgets/prefs.py index 98a0fa8c95..70edeb5e6c 100644 --- a/cola/widgets/prefs.py +++ b/cola/widgets/prefs.py @@ -112,12 +112,12 @@ def __init__(self, model, parent, source): self.merge_verbosity = QtGui.QSpinBox() self.merge_verbosity.setMinimum(0) self.merge_verbosity.setMaximum(5) - self.merge_verbosity.setProperty('value', QtCore.QVariant(5)) + self.merge_verbosity.setProperty('value', 5) self.diff_context = QtGui.QSpinBox() self.diff_context.setMinimum(2) self.diff_context.setMaximum(99) - self.diff_context.setProperty('value', QtCore.QVariant(5)) + self.diff_context.setProperty('value', 5) self.merge_summary = qtutils.checkbox(checked=True) self.merge_diffstat = qtutils.checkbox(checked=True) @@ -156,7 +156,7 @@ def __init__(self, model, parent): self.font_size = QtGui.QSpinBox() self.font_size.setMinimum(8) - self.font_size.setProperty('value', QtCore.QVariant(12)) + self.font_size.setProperty('value', 12) self._font_str = None self.tabwidth = QtGui.QSpinBox() diff --git a/test/i18n_test.py b/test/i18n_test.py index 084cd615d3..b2c5448312 100644 --- a/test/i18n_test.py +++ b/test/i18n_test.py @@ -1,14 +1,9 @@ from __future__ import absolute_import, division, unicode_literals -import os import unittest -import sip - -if not os.getenv('GIT_COLA_NO_SIP_SETAPI', False): - sip.setapi('QString', 1) - -from PyQt4 import QtCore +from cola import sipcompat +sipcompat.initialize() from cola import i18n from cola.i18n import N_ @@ -62,18 +57,6 @@ def test_translates_random_english(self): actual = N_('Random') self.assertEqual(expect, actual) - def test_guards_against_qstring(self): - """Test that random QString is passed through as-is - """ - i18n.install('en_US.UTF-8') - expect = 'Random' - if hasattr(QtCore, 'QString'): - text = QtCore.QString('Random') - else: - text = 'Random' - actual = i18n.gettext(text) - self.assertEqual(expect, actual) - if __name__ == '__main__': unittest.main()