Blob Blame History Raw
From c108980a24445f53bc2d8ffa2799fb323628f73b Mon Sep 17 00:00:00 2001
From: Andrei Kopats <hlamer@tut.by>
Date: Sun, 22 Jun 2014 11:42:09 +0300
Subject: [PATCH 1/4] Use Python 3

---
 README.md                                         |   4 +-
 debian/control                                    |   6 +-
 doc/source/conf.py                                |  16 ++--
 editor.py                                         |   4 +-
 profiling/typing_performance_test.py              |  12 +--
 qutepart/__init__.py                              |  12 +--
 qutepart/indenter/cstyle.py                       |   2 +-
 qutepart/lines.py                                 |   2 +-
 qutepart/rectangularselection.py                  |  13 +--
 qutepart/syntax/__init__.py                       |   2 +-
 qutepart/syntax/data/regenerate-definitions-db.py |   4 +-
 qutepart/syntax/loader.py                         |  28 +++---
 qutepart/syntax/parser.py                         |  10 +--
 qutepart/vim.py                                   |   8 +-
 rpm/python-qutepart.spec                          |  16 ++--
 setup.py                                          |  30 +++----
 tests/run_all.py                                  |   2 +-
 tests/test_actions.py                             |   2 +-
 tests/test_api.py                                 | 100 +++++++++++-----------
 tests/test_bookmarks.py                           |   2 +-
 tests/test_bracket_hlighter.py                    |   2 +-
 tests/test_completion.py                          |   2 +-
 tests/test_draw_whitespace.py                     |   6 +-
 tests/test_edit.py                                |   2 +-
 tests/test_indent.py                              |   2 +-
 tests/test_indenter/indenttest.py                 |   2 +-
 tests/test_indenter/test_cstyle.py                |   5 +-
 tests/test_indenter/test_haskell.py               |   6 +-
 tests/test_indenter/test_lisp.py                  |   5 +-
 tests/test_indenter/test_normal.py                |   5 +-
 tests/test_indenter/test_python.py                |   5 +-
 tests/test_indenter/test_ruby.py                  |   5 +-
 tests/test_indenter/test_scheme.py                |   5 +-
 tests/test_indenter/test_xmlindent.py             |   5 +-
 tests/test_rectangular_selection.py               |   6 +-
 tests/test_syntax/test_dynamic_substitution.py    |   2 +-
 tests/test_syntax/test_rules.py                   |  18 ++--
 tests/test_syntax/test_xml_definition_parsing.py  |   2 +-
 tests/test_vim.py                                 |   4 +-
 tools/show-syntax.py                              |   6 +-
 40 files changed, 198 insertions(+), 172 deletions(-)

diff --git a/README.md b/README.md
index 66ae1f3..27e1256 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Component has been created for [Enki editor](http://enki-editor.org) as replacem
 
 Qutepart depends on:
 
-* Python 2.7
+* Python 3
 * PyQt4 (see *Known problems* section)
 * pcre
 
@@ -29,7 +29,7 @@ On Debian, Ubuntu and other Linuxes install package `libpcreX-dev`, where `X` is
 For other OSes - see instructions on pcre website
 
 #### 2. Install Python development files
-On Debian, Ubuntu and other Linuxes install package `python-dev`, on other systems - see Python website
+On Debian, Ubuntu and other Linuxes install package `python3-dev`, on other systems - see Python website
 
 #### 3. Install C compiler
 It will probably be gcc
diff --git a/debian/control b/debian/control
index 492fbbe..22c29a7 100644
--- a/debian/control
+++ b/debian/control
@@ -2,16 +2,16 @@ Source: python-qutepart
 Maintainer: Andrei Kopats <hlamer@tut.by>
 Section: python
 Priority: optional
-Build-Depends: debhelper (>= 7.4.1), python2.7, libpcre3-dev, python2.7-dev, python-qt4, python-setuptools
+Build-Depends: debhelper (>= 7.4.1), python3, libpcre3-dev, python3-dev, python3-qt5, python3-setuptools
 X-Python-Version: >= 2.7
 Standards-Version: 3.9.4
 Homepage: https://github.com/hlamer/qutepart
 Vcs-Git: https://github.com/hlamer/qutepart
 Vcs-Browser: https://github.com/hlamer/qutepart
 
-Package: python-qutepart
+Package: python3-qutepart
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-qt4, libpcre3
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python3-pyqt4, libpcre3
 Description: Code editor component
  Some of the features:
   * Syntax highlighting for 196 languages
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 7f81195..1eff823 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -75,8 +75,8 @@ def __getattr__(self, name):
 master_doc = 'index'
 
 # General information about the project.
-project = u'qutepart'
-copyright = u'2013, Andrei Kopats'
+project = 'qutepart'
+copyright = '2013, Andrei Kopats'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -219,8 +219,8 @@ def __getattr__(self, name):
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'qutepart.tex', u'qutepart Documentation',
-   u'Andrei Kopats', 'manual'),
+  ('index', 'qutepart.tex', 'qutepart Documentation',
+   'Andrei Kopats', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -251,8 +251,8 @@ def __getattr__(self, name):
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'qutepart', u'qutepart Documentation',
-     [u'Andrei Kopats'], 1)
+    ('index', 'qutepart', 'qutepart Documentation',
+     ['Andrei Kopats'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -265,8 +265,8 @@ def __getattr__(self, name):
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'qutepart', u'qutepart Documentation',
-   u'Andrei Kopats', 'qutepart', 'One line description of project.',
+  ('index', 'qutepart', 'qutepart Documentation',
+   'Andrei Kopats', 'qutepart', 'One line description of project.',
    'Miscellaneous'),
 ]
 
diff --git a/editor.py b/editor.py
index ba7c411..a4ba78e 100755
--- a/editor.py
+++ b/editor.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 import os
@@ -38,7 +38,7 @@ def main():
     import qutepart  # after correct sys.path has been set
 
     with open(ns.file) as file:
-        text = unicode(file.read(), 'utf8')
+        text = file.read()
 
     if ns.debug:
         logging.getLogger('qutepart').setLevel(logging.DEBUG)
diff --git a/profiling/typing_performance_test.py b/profiling/typing_performance_test.py
index 2812272..427ad52 100755
--- a/profiling/typing_performance_test.py
+++ b/profiling/typing_performance_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 import time
@@ -17,7 +17,7 @@
 
 q = qutepart.Qutepart()
 q.detectSyntax(sourceFilePath=sys.argv[1])
-print 'Language:', q.language()
+print('Language:', q.language())
 
 q.showMaximized()
 
@@ -29,7 +29,7 @@
 def click(key):
     clockBefore = time.clock()
 
-    if isinstance(key, basestring):
+    if isinstance(key, str):
         QTest.keyClicks(q, key)
     else:
         QTest.keyClick(q, key)
@@ -56,11 +56,11 @@ def doTest():
 
     clockAfter = time.clock()
     typingTime = clockAfter - clockBefore
-    print 'Typed {} chars in {} sec. {} ms per character'.format(len(text), typingTime, typingTime * 1000 / len(text))
-    print 'Time per click: count of clicks'
+    print('Typed {} chars in {} sec. {} ms per character'.format(len(text), typingTime, typingTime * 1000 / len(text)))
+    print('Time per click: count of clicks')
     clickTimeKeys = sorted(clickTimes.keys())
     for ckt in clickTimeKeys:
-        print '       %5dms:            %4d' % (ckt, clickTimes[ckt])
+        print('       %5dms:            %4d' % (ckt, clickTimes[ckt]))
 
     app.quit()
 
diff --git a/qutepart/__init__.py b/qutepart/__init__.py
index 298b3e5..9b1926b 100644
--- a/qutepart/__init__.py
+++ b/qutepart/__init__.py
@@ -221,12 +221,12 @@ class Qutepart(QPlainTextEdit):
     **Public methods**
     '''
 
-    userWarning = pyqtSignal(unicode)
-    languageChanged = pyqtSignal(unicode)
+    userWarning = pyqtSignal(str)
+    languageChanged = pyqtSignal(str)
     indentWidthChanged = pyqtSignal(int)
     indentUseTabsChanged = pyqtSignal(bool)
-    eolChanged = pyqtSignal(unicode)
-    vimModeIndicationChanged = pyqtSignal(QColor, unicode)
+    eolChanged = pyqtSignal(str)
+    vimModeIndicationChanged = pyqtSignal(QColor, str)
     vimModeEnabledChanged = pyqtSignal(bool)
 
     LINT_ERROR = 'e'
@@ -434,7 +434,7 @@ def lines(self):
     @lines.setter
     def lines(self, value):
         if not isinstance(value, (list, tuple)) or \
-           not all([isinstance(item, basestring) for item in value]):
+           not all([isinstance(item, str) for item in value]):
             raise TypeError('Invalid new value of "lines" attribute')
         self.setPlainText('\n'.join(value))
 
@@ -467,7 +467,7 @@ def selectedText(self):
         text = self.textCursor().selectedText()
 
         # replace unicode paragraph separator with habitual \n
-        text = text.replace(u'\u2029', '\n')
+        text = text.replace('\u2029', '\n')
 
         return text
 
diff --git a/qutepart/indenter/cstyle.py b/qutepart/indenter/cstyle.py
index f1f38fb..a470838 100644
--- a/qutepart/indenter/cstyle.py
+++ b/qutepart/indenter/cstyle.py
@@ -20,7 +20,7 @@
 
 def dbg(*args):
     if (DEBUG_MODE):
-        print args
+        print(args)
 
 #global variables and functions
 
diff --git a/qutepart/lines.py b/qutepart/lines.py
index 537dd49..f1c1482 100644
--- a/qutepart/lines.py
+++ b/qutepart/lines.py
@@ -137,7 +137,7 @@ def __init__(self, block):
         def __iter__(self):
             return self
 
-        def next(self):
+        def __next__(self):
             if self._block.isValid():
                 self._block, result = self._block.next(), self._block.text()
                 return result
diff --git a/qutepart/rectangularselection.py b/qutepart/rectangularselection.py
index 74d0004..730e717 100644
--- a/qutepart/rectangularselection.py
+++ b/qutepart/rectangularselection.py
@@ -82,7 +82,7 @@ def _visibleCharPositionGenerator(self, text):
             if char == '\t':
                 currentPos += self._qpart.indentWidth
                 # trim reminder. If width('\t') == 4,   width('abc\t') == 4
-                currentPos = currentPos / self._qpart.indentWidth * self._qpart.indentWidth
+                currentPos = currentPos // self._qpart.indentWidth * self._qpart.indentWidth
             else:
                 currentPos += 1
             yield currentPos
@@ -93,8 +93,9 @@ def _realToVisibleColumn(self, text, realColumn):
         """
         generator = self._visibleCharPositionGenerator(text)
         for i in range(realColumn):
-            val = generator.next()
-        return generator.next()
+            val = next(generator)
+        val = next(generator)
+        return val
 
     def _visibleToRealColumn(self, text, visiblePos):
         """If \t is used, real position of symbol in block and visible position differs
@@ -203,10 +204,10 @@ def _indentUpTo(self, text, width):
             return ''
         elif self._qpart.indentUseTabs and \
            all([char == '\t' for char in text]):  # if using tabs and only tabs in text
-            return '\t' * (diff / self._qpart.indentWidth) + \
+            return '\t' * (diff // self._qpart.indentWidth) + \
                    ' ' * (diff % self._qpart.indentWidth)
         else:
-            return ' ' * diff
+            return ' ' * int(diff)
 
     def paste(self, mimeData):
         """Paste recrangular selection.
@@ -217,7 +218,7 @@ def paste(self, mimeData):
         elif self._qpart.textCursor().hasSelection():
             self._qpart.textCursor().deleteChar()
 
-        text = str(mimeData.data(self.MIME_TYPE)).decode('utf8')
+        text = bytes(mimeData.data(self.MIME_TYPE)).decode('utf8')
         lines = text.splitlines()
         cursorLine, cursorCol = self._qpart.cursorPosition
         if cursorLine + len(lines) > len(self._qpart.lines):
diff --git a/qutepart/syntax/__init__.py b/qutepart/syntax/__init__.py
index 24b59fd..ca34c40 100644
--- a/qutepart/syntax/__init__.py
+++ b/qutepart/syntax/__init__.py
@@ -82,7 +82,7 @@ def __str__(self):
         res += ' license: %s\n' % self.license
         res += ' hidden: %s\n' % self.hidden
         res += ' indenter: %s\n' % self.indenter
-        res += unicode(self.parser)
+        res += str(self.parser)
 
         return res
 
diff --git a/qutepart/syntax/data/regenerate-definitions-db.py b/qutepart/syntax/data/regenerate-definitions-db.py
index c4cc525..1d649cf 100755
--- a/qutepart/syntax/data/regenerate-definitions-db.py
+++ b/qutepart/syntax/data/regenerate-definitions-db.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os.path
 import json
@@ -81,7 +81,7 @@ def main():
     with open('syntax_db.json', 'w') as syntaxDbFile:
         json.dump(result, syntaxDbFile, sort_keys=True, indent=4)
 
-    print 'Done. Do not forget to commit the changes'
+    print('Done. Do not forget to commit the changes')
 
 if __name__ == '__main__':
     main()
diff --git a/qutepart/syntax/loader.py b/qutepart/syntax/loader.py
index 5b2b489..63226f5 100644
--- a/qutepart/syntax/loader.py
+++ b/qutepart/syntax/loader.py
@@ -93,7 +93,7 @@ def _parseBoolAttribute(value):
 
 def _safeGetRequiredAttribute(xmlElement, name, default):
     if name in xmlElement.attrib:
-        return unicode(xmlElement.attrib[name])
+        return str(xmlElement.attrib[name])
     else:
         _logger.warning("Required attribute '%s' is not set for element '%s'", name, xmlElement.tag)
         return default
@@ -233,7 +233,7 @@ def _loadDetectChar(parentContext, xmlElement, attributeToFormatMap, formatConve
             _logger.warning('Too little DetectChar index %d', index)
             index = 0
 
-    return _parserModule.DetectChar(abstractRuleParams, unicode(char), index)
+    return _parserModule.DetectChar(abstractRuleParams, str(char), index)
 
 def _loadDetect2Chars(parentContext, xmlElement, attributeToFormatMap, formatConverterFunction):
     char = _safeGetRequiredAttribute(xmlElement, 'char', None)
@@ -286,11 +286,11 @@ def _processCraracterCodes(text):
         i.e. \0377 is character with code 255 in the unicode table
         Convert such notation to unicode text
         """
-        text = unicode(text)
+        text = str(text)
         def replFunc(matchObj):
             matchText = matchObj.group(0)
-            charCode = eval(matchText[1:])
-            return chr(charCode).decode('latin1')
+            charCode = eval('0o' + matchText[2:])
+            return chr(charCode)
         return re.sub(r"\\0\d\d\d", replFunc, text)
 
     insensitive = _parseBoolAttribute(xmlElement.attrib.get('insensitive', 'false'))
@@ -365,8 +365,8 @@ def _loadContexts(highlightingElement, parser, attributeToFormatMap, formatConve
     contextList = []
     for xmlElement in xmlElementList:
         name = _safeGetRequiredAttribute(xmlElement,
-                                         u'name',
-                                         u'Error: context name is not set!!!')
+                                         'name',
+                                         'Error: context name is not set!!!')
         context = _parserModule.Context(parser, name)
         contextList.append(context)
 
@@ -448,7 +448,7 @@ def _makeFormat(defaultTheme, defaultStyleName, textType, item=None):
 
     if item is not None:
         caseInsensitiveAttributes = {}
-        for key, value in item.attrib.iteritems():
+        for key, value in item.attrib.items():
             caseInsensitiveAttributes[key.lower()] = value.lower()
 
         if 'color' in caseInsensitiveAttributes:
@@ -503,7 +503,7 @@ def _loadLists(root, highlightingElement):
     lists = {}  # list name: list
     for listElement in highlightingElement.findall('list'):
         # Sometimes item.text is none. Broken xml files
-        items = [unicode(item.text.strip()) \
+        items = [str(item.text.strip()) \
                     for item in listElement.findall('item') \
                         if item.text is not None]
         name = _safeGetRequiredAttribute(listElement, 'name', 'Error: list name is not set!!!')
@@ -520,9 +520,9 @@ def _makeKeywordsLowerCase(listDict):
 def _loadSyntaxDescription(root, syntax):
     syntax.name = _safeGetRequiredAttribute(root, 'name', 'Error: .parser name is not set!!!')
     syntax.section = _safeGetRequiredAttribute(root, 'section', 'Error: Section is not set!!!')
-    syntax.extensions = filter(None, _safeGetRequiredAttribute(root, 'extensions', '').split(';'))
-    syntax.firstLineGlobs = filter(None, root.attrib.get('firstLineGlobs', '').split(';'))
-    syntax.mimetype = filter(None, root.attrib.get('mimetype', '').split(';'))
+    syntax.extensions = [_f for _f in _safeGetRequiredAttribute(root, 'extensions', '').split(';') if _f]
+    syntax.firstLineGlobs = [_f for _f in root.attrib.get('firstLineGlobs', '').split(';') if _f]
+    syntax.mimetype = [_f for _f in root.attrib.get('mimetype', '').split(';') if _f]
     syntax.version = root.attrib.get('version', None)
     syntax.kateversion = root.attrib.get('kateversion', None)
     syntax.priority = int(root.attrib.get('priority', '0'))
@@ -540,7 +540,7 @@ def loadSyntax(syntax, filePath, formatConverterFunction = None):
         try:
             root = xml.etree.ElementTree.parse(definitionFile).getroot()
         except Exception as ex:
-            print >> sys.stderr, 'When opening %s:' % filePath
+            print('When opening %s:' % filePath, file=sys.stderr)
             raise
 
     highlightingElement = root.find('highlighting')
@@ -579,7 +579,7 @@ def loadSyntax(syntax, filePath, formatConverterFunction = None):
            'mode' in indentationElement.attrib:
             syntax.indenter = indentationElement.attrib['mode']
 
-    deliminatorSetAsString = u''.join(list(deliminatorSet))
+    deliminatorSetAsString = ''.join(list(deliminatorSet))
     debugOutputEnabled = _logger.isEnabledFor(logging.DEBUG)  # for cParser
     parser = _parserModule.Parser(syntax, deliminatorSetAsString, lists, keywordsCaseSensitive, debugOutputEnabled)
     syntax._setParser(parser)
diff --git a/qutepart/syntax/parser.py b/qutepart/syntax/parser.py
index e1ad239..b7de532 100644
--- a/qutepart/syntax/parser.py
+++ b/qutepart/syntax/parser.py
@@ -819,7 +819,7 @@ def __str__(self):
         res += '\t\t%s: %s\n' % ('dynamic', self.dynamic)
 
         for rule in self.rules:
-            res += unicode(rule)
+            res += str(rule)
         return res
 
     def parseBlock(self, contextStack, currentColumnIndex, text):
@@ -917,8 +917,8 @@ def __str__(self):
         """Serialize.
         For debug logs
         """
-        res = u'Parser\n'
-        for name, value in vars(self).iteritems():
+        res = 'Parser\n'
+        for name, value in vars(self).items():
             if not name.startswith('_') and \
                not name in ('defaultContext', 'deliminatorSet', 'contexts', 'lists', 'syntax') and \
                not value is None:
@@ -926,12 +926,12 @@ def __str__(self):
 
         res += '\tDefault context: %s\n' % self.defaultContext.name
 
-        for listName, listValue in self.lists.iteritems():
+        for listName, listValue in self.lists.items():
             res += '\tList %s: %s\n' % (listName, listValue)
 
 
         for context in self.contexts.values():
-            res += unicode(context)
+            res += str(context)
 
         return res
 
diff --git a/qutepart/vim.py b/qutepart/vim.py
index b7f9968..fa74e3f 100644
--- a/qutepart/vim.py
+++ b/qutepart/vim.py
@@ -76,7 +76,7 @@ class Vim(QObject):
     """Vim mode implementation.
     Listens events and does actions
     """
-    modeIndicationChanged = pyqtSignal(QColor, unicode)
+    modeIndicationChanged = pyqtSignal(QColor, str)
 
     internalClipboard = ''  # delete commands save text to this clipboard
 
@@ -266,7 +266,7 @@ def text(self):
 
     def _reset(self):
         self._processCharCoroutine = self._processChar()
-        self._processCharCoroutine.next()  # run until the first yield
+        next(self._processCharCoroutine)  # run until the first yield
         self._typedText = ''
 
     _MOTIONS = (_0, _Home,
@@ -684,7 +684,7 @@ def cmdInternalPaste(self, cmd):
             else:
                 cursor.removeSelectedText()
 
-            if isinstance(Vim.internalClipboard, basestring):
+            if isinstance(Vim.internalClipboard, str):
                 self._qpart.textCursor().insertText(Vim.internalClipboard)
             elif isinstance(Vim.internalClipboard, list):
                 currentLineIndex = self._qpart.cursorPosition[0]
@@ -1020,7 +1020,7 @@ def cmdInternalPaste(self, cmd, count):
         if not Vim.internalClipboard:
             return
 
-        if isinstance(Vim.internalClipboard, basestring):
+        if isinstance(Vim.internalClipboard, str):
             cursor = self._qpart.textCursor()
             if cmd == _p:
                 cursor.movePosition(QTextCursor.Right)
diff --git a/rpm/python-qutepart.spec b/rpm/python-qutepart.spec
index 2bf52ec..3f97174 100644
--- a/rpm/python-qutepart.spec
+++ b/rpm/python-qutepart.spec
@@ -1,4 +1,4 @@
-Name:           python-qutepart
+Name:           python3-qutepart
 Version:        2.2.2
 Release:        1%{?dist}
 Summary:        Code editor widget for PyQt
@@ -10,18 +10,18 @@ URL:            https://github.com/hlamer/qutepart
 Source0:        https://github.com/hlamer/qutepart/archive/v%{version}.tar.gz#/qutepart-%{version}.tar.gz
 
 BuildRequires:  pcre-devel
-BuildRequires:  python-devel
-BuildRequires:  python-setuptools
-Requires:       python >= 2.7
+BuildRequires:  python3-devel
+BuildRequires:  python3-setuptools
+Requires:       python >= 3.3
 Requires:       pcre
 
 
 %if 0%{?fedora_version}
-BuildRequires:  PyQt4
-Requires:       PyQt4
+BuildRequires:  python3-PyQt4
+Requires:       python3-PyQt4
 %else
-BuildRequires:  python-qt4
-Requires:       python-qt4
+BuildRequires:  python3-qt4
+Requires:       python3-qt4
 %endif
 
 
diff --git a/setup.py b/setup.py
index fbd77dd..5b26ba1 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 import os
@@ -63,19 +63,19 @@ def _checkDependencies():
     There should be better way to check, if C compiler is installed
     """
     if not compiler.has_function('rand', includes = ['stdlib.h']):
-        print "It seems like C compiler is not installed or not operable."
+        print("It seems like C compiler is not installed or not operable.")
         return False
 
     if not compiler.has_function('rand',
                                  includes = ['stdlib.h', 'Python.h'],
                                  include_dirs=[distutils.sysconfig.get_python_inc()],
                                  library_dirs=[os.path.join(os.path.dirname(sys.executable), 'libs')]):
-        print "Failed to find Python headers."
-        print "Try to install python-dev package"
-        print "If not standard directories are used, pass parameters"
-        print "\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build"
-        print "\tpython setup.py install --lib-dir=/usr/local/lib --include-dir=/usr/local/include"
-        print "--lib-dir= and --include-dir= may be used multiple times"
+        print("Failed to find Python headers.")
+        print("Try to install python-dev package")
+        print("If not standard directories are used, pass parameters")
+        print("\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build")
+        print("\tpython setup.py install --lib-dir=/usr/local/lib --include-dir=/usr/local/include")
+        print("--lib-dir= and --include-dir= may be used multiple times")
         return False
 
     if not compiler.has_function('pcre_version',
@@ -83,13 +83,13 @@ def _checkDependencies():
                                  libraries = ['pcre'],
                                  include_dirs=include_dirs,
                                  library_dirs=library_dirs):
-        print "Failed to find pcre library."
-        print "Try to install libpcre{version}-dev package, or go to http://pcre.org"
-        print "If not standard directories are used, pass parameters:"
-        print "\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build"
-        print "or"
-        print "\tpython setup.py install --lib-dir=/my/local/lib --include-dir=/my/local/include"
-        print "--lib-dir= and --include-dir= may be used multiple times"
+        print("Failed to find pcre library."
+        print("Try to install libpcre{version}-dev package, or go to http://pcre.org")
+        print("If not standard directories are used, pass parameters:")
+        print("\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build")
+        print("or")
+        print("\tpython setup.py install --lib-dir=/my/local/lib --include-dir=/my/local/include")
+        print("--lib-dir= and --include-dir= may be used multiple times")
         return False
 
     return True
diff --git a/tests/run_all.py b/tests/run_all.py
index 3578404..5c9a4d2 100755
--- a/tests/run_all.py
+++ b/tests/run_all.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 import base
diff --git a/tests/test_actions.py b/tests/test_actions.py
index e5b20e2..9b65857 100755
--- a/tests/test_actions.py
+++ b/tests/test_actions.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
diff --git a/tests/test_api.py b/tests/test_api.py
index 2eff704..09efda6 100755
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
@@ -41,50 +41,50 @@ def test_setSelection(self):
 
         self.qpart.selectedPosition = ((0, 3), (0, 7))
 
-        self.assertEquals(self.qpart.selectedText, "f fd")
-        self.assertEquals(self.qpart.selectedPosition, ((0, 3), (0, 7)))
+        self.assertEqual(self.qpart.selectedText, "f fd")
+        self.assertEqual(self.qpart.selectedPosition, ((0, 3), (0, 7)))
 
     def test_selected_multiline_text(self):
         self.qpart.text = "a\nb"
         self.qpart.selectedPosition = ((0, 0), (1, 1))
-        self.assertEquals(self.qpart.selectedText, "a\nb")
+        self.assertEqual(self.qpart.selectedText, "a\nb")
 
 class ReplaceText(_BaseTest):
     def test_replaceText1(self):
         # Basic case
         self.qpart.text = '123456789'
         self.qpart.replaceText(3, 4, 'xyz')
-        self.assertEquals(self.qpart.text, '123xyz89')
+        self.assertEqual(self.qpart.text, '123xyz89')
 
     def test_replaceText2(self):
         # Replace uses (line, col) position
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText((1, 4), 3, 'Z')
-        self.assertEquals(self.qpart.text, '12345\n6789Zbcde')
+        self.assertEqual(self.qpart.text, '12345\n6789Zbcde')
 
     def test_replaceText3(self):
         # Edge cases
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText((0, 0), 3, 'Z')
-        self.assertEquals(self.qpart.text, 'Z45\n67890\nabcde')
+        self.assertEqual(self.qpart.text, 'Z45\n67890\nabcde')
 
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText((2, 4), 1, 'Z')
-        self.assertEquals(self.qpart.text, '12345\n67890\nabcdZ')
+        self.assertEqual(self.qpart.text, '12345\n67890\nabcdZ')
 
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText((0, 0), 0, 'Z')
-        self.assertEquals(self.qpart.text, 'Z12345\n67890\nabcde')
+        self.assertEqual(self.qpart.text, 'Z12345\n67890\nabcde')
 
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText((2, 5), 0, 'Z')
-        self.assertEquals(self.qpart.text, '12345\n67890\nabcdeZ')
+        self.assertEqual(self.qpart.text, '12345\n67890\nabcdeZ')
 
     def test_replaceText4(self):
         # Replace nothing with something
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.replaceText(2, 0, 'XYZ')
-        self.assertEquals(self.qpart.text, '12XYZ345\n67890\nabcde')
+        self.assertEqual(self.qpart.text, '12XYZ345\n67890\nabcde')
 
     def test_replaceText5(self):
         # Make sure exceptions are raised for invalid params
@@ -101,23 +101,23 @@ def test_1(self):
         # Basic case
         self.qpart.text = '123456789'
         self.qpart.insertText(3, 'xyz')
-        self.assertEquals(self.qpart.text, '123xyz456789')
+        self.assertEqual(self.qpart.text, '123xyz456789')
 
     def test_2(self):
         # (line, col) position
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.insertText((1, 4), 'Z')
-        self.assertEquals(self.qpart.text, '12345\n6789Z0\nabcde')
+        self.assertEqual(self.qpart.text, '12345\n6789Z0\nabcde')
 
     def test_3(self):
         # Edge cases
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.insertText((0, 0), 'Z')
-        self.assertEquals(self.qpart.text, 'Z12345\n67890\nabcde')
+        self.assertEqual(self.qpart.text, 'Z12345\n67890\nabcde')
 
         self.qpart.text = '12345\n67890\nabcde'
         self.qpart.insertText((2, 5), 'Z')
-        self.assertEquals(self.qpart.text, '12345\n67890\nabcdeZ')
+        self.assertEqual(self.qpart.text, '12345\n67890\nabcdeZ')
 
 
 class IsCodeOrComment(_BaseTest):
@@ -129,10 +129,10 @@ def test_1(self):
         self.qpart.text = 'a + b # comment'
         self.qpart.detectSyntax(language = 'Python')
         self._wait_highlighting_finished()
-        self.assertEquals([self.qpart.isCode(0, i) for i in range(len(self.qpart.text))],
+        self.assertEqual([self.qpart.isCode(0, i) for i in range(len(self.qpart.text))],
                           [True, True, True, True, True, True, False, False, False, False, \
                            False, False, False, False, False])
-        self.assertEquals([self.qpart.isComment(0, i) for i in range(len(self.qpart.text))],
+        self.assertEqual([self.qpart.isComment(0, i) for i in range(len(self.qpart.text))],
                           [False, False, False, False, False, False, True, True, True, True, \
                           True, True, True, True, True])
 
@@ -168,19 +168,19 @@ def test_here_doc(self):
 class DetectSyntax(_BaseTest):
     def test_1(self):
         self.qpart.detectSyntax(xmlFileName='ada.xml')
-        self.assertEquals(self.qpart.language(), 'Ada')
+        self.assertEqual(self.qpart.language(), 'Ada')
 
         self.qpart.detectSyntax(mimeType='text/x-cgsrc')
-        self.assertEquals(self.qpart.language(), 'Cg')
+        self.assertEqual(self.qpart.language(), 'Cg')
 
         self.qpart.detectSyntax(language='CSS')
-        self.assertEquals(self.qpart.language(), 'CSS')
+        self.assertEqual(self.qpart.language(), 'CSS')
 
         self.qpart.detectSyntax(sourceFilePath='/tmp/file.feh')
-        self.assertEquals(self.qpart.language(), 'ferite')
+        self.assertEqual(self.qpart.language(), 'ferite')
 
         self.qpart.detectSyntax(firstLine='<?php hello() ?>')
-        self.assertEquals(self.qpart.language(), 'PHP (HTML)')
+        self.assertEqual(self.qpart.language(), 'PHP (HTML)')
 
 
 class Signals(_BaseTest):
@@ -191,7 +191,7 @@ def setNeVal(val):
         self.qpart.languageChanged.connect(setNeVal)
 
         self.qpart.detectSyntax(language='Python')
-        self.assertEquals(newValue[0], 'Python')
+        self.assertEqual(newValue[0], 'Python')
 
     def test_indent_width_changed(self):
         newValue = [None]
@@ -200,7 +200,7 @@ def setNeVal(val):
         self.qpart.indentWidthChanged.connect(setNeVal)
 
         self.qpart.indentWidth = 7
-        self.assertEquals(newValue[0], 7)
+        self.assertEqual(newValue[0], 7)
 
     def test_use_tabs_changed(self):
         newValue = [None]
@@ -210,7 +210,7 @@ def setNeVal(val):
         self.qpart.indentUseTabsChanged.connect(setNeVal)
 
         self.qpart.indentUseTabs = True
-        self.assertEquals(newValue[0], True)
+        self.assertEqual(newValue[0], True)
 
     def test_eol_changed(self):
         newValue = [None]
@@ -220,7 +220,7 @@ def setNeVal(val):
         self.qpart.eolChanged.connect(setNeVal)
 
         self.qpart.eol = '\r\n'
-        self.assertEquals(newValue[0], '\r\n')
+        self.assertEqual(newValue[0], '\r\n')
 
 
 class Completion(_BaseTest):
@@ -268,27 +268,27 @@ def setUp(self):
         self.qpart.text = 'abcd\nefgh\nklmn\nopqr'
 
     def test_accessByIndex(self):
-        self.assertEquals(self.qpart.lines[0], 'abcd')
-        self.assertEquals(self.qpart.lines[1], 'efgh')
-        self.assertEquals(self.qpart.lines[-1], 'opqr')
+        self.assertEqual(self.qpart.lines[0], 'abcd')
+        self.assertEqual(self.qpart.lines[1], 'efgh')
+        self.assertEqual(self.qpart.lines[-1], 'opqr')
 
     def test_modifyByIndex(self):
         self.qpart.lines[2] = 'new text'
-        self.assertEquals(self.qpart.text, 'abcd\nefgh\nnew text\nopqr')
+        self.assertEqual(self.qpart.text, 'abcd\nefgh\nnew text\nopqr')
 
     def test_getSlice(self):
-        self.assertEquals(self.qpart.lines[0], 'abcd')
-        self.assertEquals(self.qpart.lines[1], 'efgh')
-        self.assertEquals(self.qpart.lines[3], 'opqr')
-        self.assertEquals(self.qpart.lines[-4], 'abcd')
-        self.assertEquals(self.qpart.lines[1:4], ['efgh', 'klmn', 'opqr'])
-        self.assertEquals(self.qpart.lines[1:7], ['efgh', 'klmn', 'opqr'])  # Python list behaves this way
-        self.assertEquals(self.qpart.lines[0:0], [])
-        self.assertEquals(self.qpart.lines[0:1], ['abcd'])
-        self.assertEquals(self.qpart.lines[:2], ['abcd', 'efgh'])
-        self.assertEquals(self.qpart.lines[0:-2], ['abcd', 'efgh'])
-        self.assertEquals(self.qpart.lines[-2:], ['klmn', 'opqr'])
-        self.assertEquals(self.qpart.lines[-4:-2], ['abcd', 'efgh'])
+        self.assertEqual(self.qpart.lines[0], 'abcd')
+        self.assertEqual(self.qpart.lines[1], 'efgh')
+        self.assertEqual(self.qpart.lines[3], 'opqr')
+        self.assertEqual(self.qpart.lines[-4], 'abcd')
+        self.assertEqual(self.qpart.lines[1:4], ['efgh', 'klmn', 'opqr'])
+        self.assertEqual(self.qpart.lines[1:7], ['efgh', 'klmn', 'opqr'])  # Python list behaves this way
+        self.assertEqual(self.qpart.lines[0:0], [])
+        self.assertEqual(self.qpart.lines[0:1], ['abcd'])
+        self.assertEqual(self.qpart.lines[:2], ['abcd', 'efgh'])
+        self.assertEqual(self.qpart.lines[0:-2], ['abcd', 'efgh'])
+        self.assertEqual(self.qpart.lines[-2:], ['klmn', 'opqr'])
+        self.assertEqual(self.qpart.lines[-4:-2], ['abcd', 'efgh'])
 
         with self.assertRaises(IndexError):
             self.qpart.lines[4]
@@ -297,27 +297,27 @@ def test_getSlice(self):
 
     def test_setSlice_1(self):
         self.qpart.lines[0] = 'xyz'
-        self.assertEquals(self.qpart.text, 'xyz\nefgh\nklmn\nopqr')
+        self.assertEqual(self.qpart.text, 'xyz\nefgh\nklmn\nopqr')
 
     def test_setSlice_2(self):
         self.qpart.lines[1] = 'xyz'
-        self.assertEquals(self.qpart.text, 'abcd\nxyz\nklmn\nopqr')
+        self.assertEqual(self.qpart.text, 'abcd\nxyz\nklmn\nopqr')
 
     def test_setSlice_3(self):
         self.qpart.lines[-4] = 'xyz'
-        self.assertEquals(self.qpart.text, 'xyz\nefgh\nklmn\nopqr')
+        self.assertEqual(self.qpart.text, 'xyz\nefgh\nklmn\nopqr')
 
     def test_setSlice_4(self):
         self.qpart.lines[0:4] = ['st', 'uv', 'wx', 'z']
-        self.assertEquals(self.qpart.text, 'st\nuv\nwx\nz')
+        self.assertEqual(self.qpart.text, 'st\nuv\nwx\nz')
 
     def test_setSlice_5(self):
         self.qpart.lines[0:47] = ['st', 'uv', 'wx', 'z']
-        self.assertEquals(self.qpart.text, 'st\nuv\nwx\nz')
+        self.assertEqual(self.qpart.text, 'st\nuv\nwx\nz')
 
     def test_setSlice_6(self):
         self.qpart.lines[1:3] = ['st', 'uv']
-        self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr')
+        self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr')
 
     def test_setSlice_61(self):
         with self.assertRaises(ValueError):
@@ -325,11 +325,11 @@ def test_setSlice_61(self):
 
     def test_setSlice_7(self):
         self.qpart.lines[-3:3] = ['st', 'uv']
-        self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr')
+        self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr')
 
     def test_setSlice_8(self):
         self.qpart.lines[-3:-1] = ['st', 'uv']
-        self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr')
+        self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr')
 
     def test_setSlice_9(self):
         with self.assertRaises(IndexError):
diff --git a/tests/test_bookmarks.py b/tests/test_bookmarks.py
index 2bdc034..795f4fe 100755
--- a/tests/test_bookmarks.py
+++ b/tests/test_bookmarks.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
diff --git a/tests/test_bracket_hlighter.py b/tests/test_bracket_hlighter.py
index 3c19748..8dead38 100755
--- a/tests/test_bracket_hlighter.py
+++ b/tests/test_bracket_hlighter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 848e55c..d8ea3ad 100755
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
diff --git a/tests/test_draw_whitespace.py b/tests/test_draw_whitespace.py
index e3cbbed..fa484e9 100755
--- a/tests/test_draw_whitespace.py
+++ b/tests/test_draw_whitespace.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # encoding: utf8
 
 
@@ -48,11 +48,11 @@ def _ws_test(self,
                         try:
                             self._verify(text, expectedResult)
                         except:
-                            print "Failed params:\n\tany {}\n\tincorrect {}\n\ttabs {}\n\twidth {}".format(
+                            print("Failed params:\n\tany {}\n\tincorrect {}\n\ttabs {}\n\twidth {}".format(
                                 self.qpart.drawAnyWhitespace,
                                 self.qpart.drawIncorrectIndentation,
                                 self.qpart.indentUseTabs,
-                                self.qpart.indentWidth)
+                                self.qpart.indentWidth))
                             raise
 
     def _verify(self, text, expectedResult):
diff --git a/tests/test_edit.py b/tests/test_edit.py
index 7dda435..0f91f9c 100755
--- a/tests/test_edit.py
+++ b/tests/test_edit.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 import unittest
diff --git a/tests/test_indent.py b/tests/test_indent.py
index bc9483d..2ce5d57 100755
--- a/tests/test_indent.py
+++ b/tests/test_indent.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 import unittest
diff --git a/tests/test_indenter/indenttest.py b/tests/test_indenter/indenttest.py
index e4f9a77..58e7a53 100644
--- a/tests/test_indenter/indenttest.py
+++ b/tests/test_indenter/indenttest.py
@@ -25,7 +25,7 @@ def setOrigin(self, text):
 
     def verifyExpected(self, text):
         lines = self.qpart.text.split('\n')
-        self.assertEquals(text, map(str, lines))
+        self.assertEquals(text, [l for l in lines])
 
     def setCursorPosition(self, line, col):
         self.qpart.cursorPosition = line, col
diff --git a/tests/test_indenter/test_cstyle.py b/tests/test_indenter/test_cstyle.py
index 91cf219..3901ce9 100755
--- a/tests/test_indenter/test_cstyle.py
+++ b/tests/test_indenter/test_cstyle.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 class BaseTestClass(IndentTest):
diff --git a/tests/test_indenter/test_haskell.py b/tests/test_indenter/test_haskell.py
index a4ad710..b98ef37 100755
--- a/tests/test_indenter/test_haskell.py
+++ b/tests/test_indenter/test_haskell.py
@@ -1,7 +1,11 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
+
 from indenttest import IndentTest
 
 
diff --git a/tests/test_indenter/test_lisp.py b/tests/test_indenter/test_lisp.py
index 14598ba..84cde3a 100755
--- a/tests/test_indenter/test_lisp.py
+++ b/tests/test_indenter/test_lisp.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 
diff --git a/tests/test_indenter/test_normal.py b/tests/test_indenter/test_normal.py
index 49e464a..5e1cd92 100755
--- a/tests/test_indenter/test_normal.py
+++ b/tests/test_indenter/test_normal.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 
diff --git a/tests/test_indenter/test_python.py b/tests/test_indenter/test_python.py
index 48f83f9..40714b8 100755
--- a/tests/test_indenter/test_python.py
+++ b/tests/test_indenter/test_python.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 
diff --git a/tests/test_indenter/test_ruby.py b/tests/test_indenter/test_ruby.py
index 34e1ec5..f810d90 100755
--- a/tests/test_indenter/test_ruby.py
+++ b/tests/test_indenter/test_ruby.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 class BaseTestClass(IndentTest):
diff --git a/tests/test_indenter/test_scheme.py b/tests/test_indenter/test_scheme.py
index 435eb3d..13324a2 100755
--- a/tests/test_indenter/test_scheme.py
+++ b/tests/test_indenter/test_scheme.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 class BaseTestClass(IndentTest):
diff --git a/tests/test_indenter/test_xmlindent.py b/tests/test_indenter/test_xmlindent.py
index dd66255..4fc6707 100755
--- a/tests/test_indenter/test_xmlindent.py
+++ b/tests/test_indenter/test_xmlindent.py
@@ -1,7 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
+import os.path
+import sys
+sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
 from indenttest import IndentTest
 
 class BaseTestClass(IndentTest):
diff --git a/tests/test_rectangular_selection.py b/tests/test_rectangular_selection.py
index 5083e84..c814ec0 100755
--- a/tests/test_rectangular_selection.py
+++ b/tests/test_rectangular_selection.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # encoding: utf8
 
 
@@ -145,7 +145,7 @@ def test_copy_paste(self):
 
     def test_copy_paste_utf8(self):
         self.qpart.show()
-        self.qpart.text = u'фыва'
+        self.qpart.text = 'фыва'
         for i in range(3):
             QTest.keyClick(self.qpart, Qt.Key_Right, Qt.AltModifier | Qt.ShiftModifier)
         QTest.keyClick(self.qpart, Qt.Key_C, Qt.ControlModifier)
@@ -155,7 +155,7 @@ def test_copy_paste_utf8(self):
         QTest.keyClick(self.qpart, Qt.Key_V, Qt.ControlModifier)
 
         self.assertEqual(self.qpart.text,
-                         u'фыва фыв')
+                         'фыва фыв')
 
     def test_paste_replace_selection(self):
         self.qpart.show()
diff --git a/tests/test_syntax/test_dynamic_substitution.py b/tests/test_syntax/test_dynamic_substitution.py
index 39b445a..45045cd 100755
--- a/tests/test_syntax/test_dynamic_substitution.py
+++ b/tests/test_syntax/test_dynamic_substitution.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 import sys
diff --git a/tests/test_syntax/test_rules.py b/tests/test_syntax/test_rules.py
index 75f2958..859afff 100755
--- a/tests/test_syntax/test_rules.py
+++ b/tests/test_syntax/test_rules.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 
@@ -24,7 +24,7 @@ def tryMatch(rule, column, text):
     return tryMatchWithData(rule, None, column, text)
 
 def tryMatchWithData(rule, contextData, column, text):
-    textToMatchObject = parser.TextToMatchObject(column, unicode(text), _currentSyntax.parser.deliminatorSet, contextData)
+    textToMatchObject = parser.TextToMatchObject(column, str(text), _currentSyntax.parser.deliminatorSet, contextData)
     ruleTryMatchResult = rule.tryMatch(textToMatchObject)
     if ruleTryMatchResult is not None:
         return ruleTryMatchResult.length
@@ -50,17 +50,17 @@ def test_DetectChar_dynamic(self):
         rule = self._getRule("perl.xml", "ip_string_6", 1)
         text = "a"
 
-        self.assertEqual(tryMatchWithData(rule, (u'a', u'b', u'c'), 0, text), 1)
-        self.assertEqual(tryMatchWithData(rule, (u'x', u'y', u'z'), 0, text), None)
+        self.assertEqual(tryMatchWithData(rule, ('a', 'b', 'c'), 0, text), 1)
+        self.assertEqual(tryMatchWithData(rule, ('x', 'y', 'z'), 0, text), None)
 
     def test_DetectChar_dynamic2(self):
         rule = self._getRule("perl.xml", "string_6", 3)
         text = "abcdXefg"
 
-        count = tryMatchWithData(rule, (u'X', u'Y', u'Z',), 0, text)
+        count = tryMatchWithData(rule, ('X', 'Y', 'Z',), 0, text)
         self.assertEqual(count, None)
 
-        count = tryMatchWithData(rule, (u'X', u'Y', u'Z',), 4, text)
+        count = tryMatchWithData(rule, ('X', 'Y', 'Z',), 4, text)
         self.assertEqual(count, 1)
 
     def test_Detect2Chars(self):
@@ -156,7 +156,7 @@ def test_RegExpr(self):
 
         rule = self._getRule('fsharp.xml', 'ModuleEnv2', 0)
         if hasattr(rule, 'regExp'):  # only on Python version
-            self.assertEqual(rule.regExp.pattern, u"[A-Z][A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\xff0-9_']*")
+            self.assertEqual(rule.regExp.pattern, "[A-Z][A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\xff0-9_']*")
 
     def test_RegExpr_slashB(self):
         rule = self._getRule('fortran.xml', 'find_numbers', 3)
@@ -291,7 +291,7 @@ def test_dynamic_reg_exp(self):
         """
         rule = self._getRule("ruby.xml", "gdl_dq_string_5", 2)  # "\s*%1"
         text = '%|a| x'
-        count = tryMatchWithData(rule, (u'blabla|', u'|', ), 3, text)
+        count = tryMatchWithData(rule, ('blabla|', '|', ), 3, text)
         self.assertEqual(count, 1)
 
     def test_dynamic_string_detect(self):
@@ -300,7 +300,7 @@ def test_dynamic_string_detect(self):
         rule = self._getRule("php.xml", "phpsource", 34)  # heredoc
         text = "<<<myheredoc"
 
-        count = tryMatchWithData(rule, (u'myheredoc',), 0, text)
+        count = tryMatchWithData(rule, ('myheredoc',), 0, text)
         self.assertEqual(count, len(text))
 
 
diff --git a/tests/test_syntax/test_xml_definition_parsing.py b/tests/test_syntax/test_xml_definition_parsing.py
index 30aa207..4249c2d 100755
--- a/tests/test_syntax/test_xml_definition_parsing.py
+++ b/tests/test_syntax/test_xml_definition_parsing.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os.path
 import unittest
diff --git a/tests/test_vim.py b/tests/test_vim.py
index 6a4ddf5..2f27fe3 100755
--- a/tests/test_vim.py
+++ b/tests/test_vim.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # encoding: utf8
 
 
@@ -36,7 +36,7 @@ def _onVimModeChanged(self, color, mode):
         self.vimMode = mode
 
     def click(self, keys):
-        if isinstance(keys, basestring):
+        if isinstance(keys, str):
             for key in keys:
                 if key.isupper() or key in '$%^<>':
                     QTest.keyClick(self.qpart, key, Qt.ShiftModifier)
diff --git a/tools/show-syntax.py b/tools/show-syntax.py
index 500d548..0008df8 100755
--- a/tools/show-syntax.py
+++ b/tools/show-syntax.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import sys
 sys.path.insert(0, '.')
@@ -11,7 +11,7 @@
 
 if __name__ == '__main__':
     if len(sys.argv) != 2:
-        print 'Usage:\n\t%s SYNTAX_FILE_NAME' % sys.argv[0]
+        print('Usage:\n\t%s SYNTAX_FILE_NAME' % sys.argv[0])
     else:
         syntax = SyntaxManager().getSyntax(xmlFileName = sys.argv[1])
-        print unicode(syntax)
+        print(str(syntax))

From 148bcd1d9743941ad40acab80e40adeaeba8734c Mon Sep 17 00:00:00 2001
From: Andrei Kopats <hlamer@tut.by>
Date: Thu, 13 Aug 2015 12:43:36 +0300
Subject: [PATCH 2/4] qt5

---
 README.md                            |  2 +-
 doc/source/conf.py                   |  4 ++--
 editor.py                            |  5 +++--
 profiling/typing_performance_test.py |  6 +++---
 qutepart/__init__.py                 | 15 ++++++++++-----
 qutepart/bookmarks.py                |  5 +++--
 qutepart/brackethlighter.py          |  5 +++--
 qutepart/completer.py                |  5 +++--
 qutepart/htmldelegate.py             | 13 +++++++------
 qutepart/indenter/__init__.py        |  2 +-
 qutepart/lines.py                    |  2 +-
 qutepart/rectangularselection.py     |  5 +++--
 qutepart/sideareas.py                |  7 ++++---
 qutepart/syntaxhlighter.py           |  6 +++---
 qutepart/vim.py                      |  5 +++--
 rpm/python-qutepart.spec             |  4 ++--
 tests/base.py                        | 22 +++++++++++-----------
 tests/test_actions.py                |  4 ++--
 tests/test_api.py                    | 10 ++++------
 tests/test_bookmarks.py              |  4 ++--
 tests/test_bracket_hlighter.py       |  4 ++--
 tests/test_completion.py             | 32 ++++++++++++++++++--------------
 tests/test_draw_whitespace.py        |  4 ++--
 tests/test_edit.py                   |  4 ++--
 tests/test_indent.py                 |  4 ++--
 tests/test_indenter/indenttest.py    |  4 ++--
 tests/test_rectangular_selection.py  |  4 ++--
 tests/test_vim.py                    |  4 ++--
 28 files changed, 103 insertions(+), 88 deletions(-)

diff --git a/README.md b/README.md
index 27e1256..2487d7c 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Component has been created for [Enki editor](http://enki-editor.org) as replacem
 Qutepart depends on:
 
 * Python 3
-* PyQt4 (see *Known problems* section)
+* PyQt5 (see *Known problems* section)
 * pcre
 
 #### 1. Install [pcre](http://www.pcre.org/) and development files
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 1eff823..e1ed48b 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -16,7 +16,7 @@
 
 
 """
-Fake PyQt4 module, for building docs on system without PyQt (rtfd.org)
+Fake PyQt5 module, for building docs on system without PyQt (rtfd.org)
 
 http://read-the-docs.readthedocs.org/en/latest/faq.html#my-project-isn-t-building-with-autodoc
 """
@@ -38,7 +38,7 @@ def __getattr__(self, name):
         else:
             return Mock()
 
-MOCK_MODULES = ['PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtWebKit']
+MOCK_MODULES = ['PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWebKit']
 for mod_name in MOCK_MODULES:
     sys.modules[mod_name] = Mock()
 
diff --git a/editor.py b/editor.py
index a4ba78e..069d084 100755
--- a/editor.py
+++ b/editor.py
@@ -8,7 +8,8 @@
 import sip
 sip.setapi('QString', 2)
 
-from PyQt4.QtGui import QApplication, QLabel, QMainWindow, QPalette, QVBoxLayout, QWidget
+from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget
+from PyQt5.QtGui import QPalette
 
 
 def _parseCommandLine():
@@ -123,7 +124,7 @@ def onVimModeChanged(color, text):
     window.resize(800, 600)
     window.show()
 
-    from PyQt4.QtCore import QTimer
+    from PyQt5.QtCore import QTimer
     if ns.quit:
         QTimer.singleShot(0, app.quit)
 
diff --git a/profiling/typing_performance_test.py b/profiling/typing_performance_test.py
index 427ad52..0d1a918 100755
--- a/profiling/typing_performance_test.py
+++ b/profiling/typing_performance_test.py
@@ -6,9 +6,9 @@
 import sip
 sip.setapi('QString', 2)
 
-from PyQt4.QtCore import QTimer, Qt
-from PyQt4.QtGui import QApplication, QPlainTextEdit
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import QTimer, Qt
+from PyQt5.QtGui import QApplication, QPlainTextEdit
+from PyQt5.QtTest import QTest
 
 import qutepart
 
diff --git a/qutepart/__init__.py b/qutepart/__init__.py
index 9b1926b..1c1aa3c 100644
--- a/qutepart/__init__.py
+++ b/qutepart/__init__.py
@@ -15,13 +15,18 @@
               'Use next code:\n\timport sip\n\tsip.setapi("QString", 2)\n'\
               'before importing Qutepart'
 
-from PyQt4.QtCore import QRect, Qt, QEvent, pyqtSignal
-from PyQt4.QtGui import QAction, QApplication, QColor, QBrush, \
-                        QDialog, QFont, \
+from PyQt5.QtCore import QRect, Qt, QEvent, pyqtSignal
+from PyQt5.QtGui import QColor, QBrush, QFont, \
                         QIcon, QKeySequence, QPainter, QPen, QPalette, \
+                        QTextCharFormat, QTextCursor, \
+                        QTextBlock, QTextFormat
+
+from PyQt5.QtWidgets import QAction, QApplication, QDialog, \
                         QPlainTextEdit, \
-                        QPrintDialog, QTextCharFormat, QTextCursor, \
-                        QTextBlock, QTextEdit, QTextFormat
+                        QShortcut,  \
+                        QTextEdit
+
+from PyQt5.QtPrintSupport import QPrintDialog
 
 from qutepart.syntax import SyntaxManager
 from qutepart.syntaxhlighter import SyntaxHighlighter
diff --git a/qutepart/bookmarks.py b/qutepart/bookmarks.py
index 27df11b..77489b1 100644
--- a/qutepart/bookmarks.py
+++ b/qutepart/bookmarks.py
@@ -1,7 +1,8 @@
 """Bookmarks functionality implementation"""
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtGui import QAction, QIcon, QKeySequence, QTextCursor
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QIcon, QKeySequence, QTextCursor
+from PyQt5.QtWidgets import QAction
 
 import qutepart
 
diff --git a/qutepart/brackethlighter.py b/qutepart/brackethlighter.py
index a308c82..f324d60 100644
--- a/qutepart/brackethlighter.py
+++ b/qutepart/brackethlighter.py
@@ -4,8 +4,9 @@
 
 import time
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtGui import QTextCursor, QTextEdit
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QTextCursor
+from PyQt5.QtWidgets import QTextEdit
 
 
 class _TimeoutException(UserWarning):
diff --git a/qutepart/completer.py b/qutepart/completer.py
index 51acd38..4c9c8a6 100644
--- a/qutepart/completer.py
+++ b/qutepart/completer.py
@@ -4,8 +4,9 @@
 import re
 import time
 
-from PyQt4.QtCore import pyqtSignal, QAbstractItemModel, QEvent, QModelIndex, QObject, QSize, Qt, QTimer, Qt
-from PyQt4.QtGui import QCursor, QListView, QStyle
+from PyQt5.QtCore import pyqtSignal, QAbstractItemModel, QEvent, QModelIndex, QObject, QSize, Qt, QTimer, Qt
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QListView, QStyle
 
 from qutepart.htmldelegate import HTMLDelegate
 
diff --git a/qutepart/htmldelegate.py b/qutepart/htmldelegate.py
index c366ace..ffc61e8 100644
--- a/qutepart/htmldelegate.py
+++ b/qutepart/htmldelegate.py
@@ -3,10 +3,11 @@
 =========================================================
 """
 
-from PyQt4.QtGui import QApplication, QAbstractTextDocumentLayout, \
-                        QStyledItemDelegate, QStyle, QStyleOptionViewItemV4, \
-                        QTextDocument, QPalette
-from PyQt4.QtCore import QSize
+from PyQt5.QtGui import QTextDocument, QPalette, QAbstractTextDocumentLayout
+from PyQt5.QtWidgets import QApplication,  \
+                        QStyledItemDelegate, QStyle, QStyleOptionViewItem
+
+from PyQt5.QtCore import QSize
 
 _HTML_ESCAPE_TABLE = \
 {
@@ -37,7 +38,7 @@ def paint(self, painter, option, index):
         """
         option.state &= ~QStyle.State_HasFocus  # never draw focus rect
 
-        options = QStyleOptionViewItemV4(option)
+        options = QStyleOptionViewItem(option)
         self.initStyleOption(options,index)
 
         style = QApplication.style() if options.widget is None else options.widget.style()
@@ -72,7 +73,7 @@ def paint(self, painter, option, index):
     def sizeHint(self, option, index):
         """QStyledItemDelegate.sizeHint implementation
         """
-        options = QStyleOptionViewItemV4(option)
+        options = QStyleOptionViewItem(option)
         self.initStyleOption(options,index)
 
         doc = QTextDocument()
diff --git a/qutepart/indenter/__init__.py b/qutepart/indenter/__init__.py
index f63e6c0..44bbc92 100644
--- a/qutepart/indenter/__init__.py
+++ b/qutepart/indenter/__init__.py
@@ -7,7 +7,7 @@
 logger = logging.getLogger('qutepart')
 
 
-from PyQt4.QtGui import QTextCursor
+from PyQt5.QtGui import QTextCursor
 
 
 def _getSmartIndenter(indenterName, qpart, indenter):
diff --git a/qutepart/lines.py b/qutepart/lines.py
index f1c1482..c1b5086 100644
--- a/qutepart/lines.py
+++ b/qutepart/lines.py
@@ -2,7 +2,7 @@
 list-like object for access text document lines
 """
 
-from PyQt4.QtGui import QTextCursor
+from PyQt5.QtGui import QTextCursor
 
 
 def _iterateBlocksFrom(block):
diff --git a/qutepart/rectangularselection.py b/qutepart/rectangularselection.py
index 730e717..f3f7732 100644
--- a/qutepart/rectangularselection.py
+++ b/qutepart/rectangularselection.py
@@ -1,5 +1,6 @@
-from PyQt4.QtCore import Qt, QMimeData
-from PyQt4.QtGui import QApplication, QKeyEvent, QKeySequence, QPalette, QTextCursor, QTextEdit, QWidget
+from PyQt5.QtCore import Qt, QMimeData
+from PyQt5.QtGui import QKeyEvent, QKeySequence, QPalette, QTextCursor
+from PyQt5.QtWidgets import QApplication, QTextEdit, QWidget
 
 
 class RectangularSelection:
diff --git a/qutepart/sideareas.py b/qutepart/sideareas.py
index 08736d0..eac36c7 100644
--- a/qutepart/sideareas.py
+++ b/qutepart/sideareas.py
@@ -1,10 +1,11 @@
 """Line numbers and bookmarks areas
 """
 
-from PyQt4.QtCore import QPoint, Qt, pyqtSignal
-from PyQt4.QtGui import QPainter, QPalette, \
+from PyQt5.QtCore import QEvent, QPoint, Qt, pyqtSignal
+from PyQt5.QtGui import QPainter, QPalette, \
                         QPixmap, \
-                        QTextBlock, QToolTip, QWidget
+                        QTextBlock
+from PyQt5.QtWidgets import QToolTip, QWidget
 
 import qutepart
 from qutepart.bookmarks import Bookmarks
diff --git a/qutepart/syntaxhlighter.py b/qutepart/syntaxhlighter.py
index 7d2f065..dee9849 100644
--- a/qutepart/syntaxhlighter.py
+++ b/qutepart/syntaxhlighter.py
@@ -4,10 +4,10 @@
 
 import time
 
-
-from PyQt4.QtCore import QObject, QTimer
-from PyQt4.QtGui import QApplication, QBrush, QColor, QFont, \
+from PyQt5.QtCore import Qt, QObject, QTimer
+from PyQt5.QtGui import QBrush, QColor, QFont, \
                         QTextBlockUserData, QTextCharFormat, QTextLayout
+from PyQt5.QtWidgets import QApplication
 
 import qutepart.syntax
 
diff --git a/qutepart/vim.py b/qutepart/vim.py
index fa74e3f..fd6f441 100644
--- a/qutepart/vim.py
+++ b/qutepart/vim.py
@@ -1,7 +1,8 @@
 import sys
 
-from PyQt4.QtCore import Qt, pyqtSignal, QObject
-from PyQt4.QtGui import QColor, QTextCursor, QTextEdit
+from PyQt5.QtCore import Qt, pyqtSignal, QObject
+from PyQt5.QtGui import QColor, QTextCursor
+from PyQt5.QtWidgets import QTextEdit
 
 
 """ This magic code sets variables like _a and _A in the global scope
diff --git a/rpm/python-qutepart.spec b/rpm/python-qutepart.spec
index 3f97174..2b55336 100644
--- a/rpm/python-qutepart.spec
+++ b/rpm/python-qutepart.spec
@@ -17,8 +17,8 @@ Requires:       pcre
 
 
 %if 0%{?fedora_version}
-BuildRequires:  python3-PyQt4
-Requires:       python3-PyQt4
+BuildRequires:  python3-PyQt5
+Requires:       python3-PyQt5
 %else
 BuildRequires:  python3-qt4
 Requires:       python3-qt4
diff --git a/tests/base.py b/tests/base.py
index b8f0350..6e89725 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -7,9 +7,9 @@
 import sip
 sip.setapi('QString', 2)
 
-from PyQt4.QtCore import Qt, QTimer
-from PyQt4.QtGui import QApplication
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt, QTimer
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtTest import QTest
 
 
 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
@@ -18,13 +18,13 @@
 # Create a single, persistent QApplication for use in all tests.
 papp = QApplication(sys.argv)
 
-def _processPendingEvents(app):
+def _processPendingEvents():
     """Process pending application events.
     Timeout is used, because on Windows hasPendingEvents() always returns True
     """
     t = time.time()
-    while app.hasPendingEvents() and (time.time() - t < 0.1):
-        app.processEvents()
+    while QApplication.instance().hasPendingEvents() and (time.time() - t < 0.1):
+        QApplication.instance().processEvents()
 
 
 def in_main_loop(func, *args):
@@ -37,18 +37,18 @@ def wrapper(*args):
 
         def execWithArgs():
             self.qpart.show()
-            QTest.qWaitForWindowShown(self.qpart)
-            _processPendingEvents(self.app)
+            QTest.qWaitForWindowExposed(self.qpart)
+            _processPendingEvents()
 
             try:
                 func(*args)
             finally:
-                _processPendingEvents(self.app)
-                self.app.quit()
+                _processPendingEvents()
+                QApplication.instance().quit()
 
         QTimer.singleShot(0, execWithArgs)
 
-        self.app.exec_()
+        QApplication.instance().exec_()
 
     wrapper.__name__ = func.__name__  # for unittest test runner
     return wrapper
diff --git a/tests/test_actions.py b/tests/test_actions.py
index 9b65857..f355848 100755
--- a/tests/test_actions.py
+++ b/tests/test_actions.py
@@ -6,8 +6,8 @@
 
 import base
 
-from PyQt4.QtTest import QTest
-from PyQt4.QtCore import Qt, QTimer
+from PyQt5.QtTest import QTest
+from PyQt5.QtCore import Qt, QTimer
 
 from qutepart import Qutepart
 
diff --git a/tests/test_api.py b/tests/test_api.py
index 09efda6..9831e16 100755
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -6,8 +6,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 
@@ -17,8 +17,6 @@
 class _BaseTest(unittest.TestCase):
     """Base class for tests
     """
-    app = base.papp  # app crashes, if created more than once
-
     def setUp(self):
         self.qpart = Qutepart()
 
@@ -122,7 +120,7 @@ def test_3(self):
 
 class IsCodeOrComment(_BaseTest):
     def _wait_highlighting_finished(self):
-        base._processPendingEvents(self.app)
+        base._processPendingEvents()
 
     def test_1(self):
         # Basic case
@@ -234,7 +232,7 @@ def setUp(self):
         super(Completion, self).setUp()
         self.qpart.text = 'completableWord\n'
         self.qpart.cursorPosition = (1, 0)
-        base._processPendingEvents(self.app)
+        base._processPendingEvents()
 
     def test_completion_enabled(self):
         self._assertNotVisible()
diff --git a/tests/test_bookmarks.py b/tests/test_bookmarks.py
index 795f4fe..31b0755 100755
--- a/tests/test_bookmarks.py
+++ b/tests/test_bookmarks.py
@@ -6,8 +6,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt, QTimer, QPoint
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt, QTimer, QPoint
+from PyQt5.QtTest import QTest
 
 
 from qutepart import Qutepart, iterateBlocksFrom
diff --git a/tests/test_bracket_hlighter.py b/tests/test_bracket_hlighter.py
index 8dead38..3ce076b 100755
--- a/tests/test_bracket_hlighter.py
+++ b/tests/test_bracket_hlighter.py
@@ -6,8 +6,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 from qutepart.brackethlighter import BracketHighlighter
diff --git a/tests/test_completion.py b/tests/test_completion.py
index d8ea3ad..a7051a4 100755
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -6,9 +6,9 @@
 
 import base
 
-from PyQt4.QtCore import Qt, QPoint
-from PyQt4.QtGui import QMainWindow
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt, QPoint
+from PyQt5.QtWidgets import QMainWindow
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 import qutepart.completer
@@ -18,7 +18,6 @@
 class Test(unittest.TestCase):
     """Base class for tests
     """
-    app = base.papp  # app crashes, if created more than once
 
     def setUp(self):
         self._window = QMainWindow()
@@ -27,13 +26,13 @@ def setUp(self):
         self._window.menuBar().addAction(self.qpart.invokeCompletionAction)
 
     def tearDown(self):
-        del self.qpart
+        self.qpart.close()
 
+    @base.in_main_loop
     def test_down_selects_first(self):
         self.qpart.text = 'aaaa\nbbbb\ncccX\ndddd\ncccY'
 
-        while self.app.hasPendingEvents():
-            self.app.processEvents()
+        QTest.qWait(1)
 
         QTest.keyClicks(self.qpart, "ccc")
         QTest.keyClick(self.qpart, Qt.Key_Down)
@@ -41,10 +40,11 @@ def test_down_selects_first(self):
         QTest.keyClick(self.qpart, Qt.Key_Enter)
         self.assertEqual(self.qpart.text, 'cccX\naaaa\nbbbb\ncccX\ndddd\ncccY')
 
+    @base.in_main_loop
     def test_down_selects_second(self):
         self.qpart.text = 'aaaa\nbbbb\ncccX\ndddd\ncccY'
 
-        base._processPendingEvents(self.app)
+        QTest.qWait(1)
 
         QTest.keyClicks(self.qpart, "ccc")
 
@@ -54,19 +54,20 @@ def test_down_selects_second(self):
         QTest.keyClick(self.qpart, Qt.Key_Enter)
         self.assertEqual(self.qpart.text, 'cccY\naaaa\nbbbb\ncccX\ndddd\ncccY')
 
-    @unittest.skip("Crashes Qt 4.8.3")
+    @unittest.skip("Fix this test sometimes")
+    @base.in_main_loop
     def test_click_selects_first(self):
         self.qpart.text = 'aaaa\nbbbb\ncccX\ndddd\ncccY'
-
         QTest.keyClicks(self.qpart, "ccc")
         QTest.mouseClick(self.qpart, Qt.LeftButton)
         QTest.keyClick(self.qpart, Qt.Key_Enter)
         self.assertEqual(self.qpart.text, 'cccY\naaaa\nbbbb\ncccX\ndddd\ncccY')
 
+    @base.in_main_loop
     def test_tab_completes(self):
         self.qpart.text = 'aaaaa\naaaaaXXXXX\n'
 
-        base._processPendingEvents(self.app)
+        QTest.qWait(1)
 
         self.qpart.cursorPosition = (2, 0)
         QTest.keyClicks(self.qpart, "aaa")
@@ -75,12 +76,13 @@ def test_tab_completes(self):
         QTest.keyClick(self.qpart, Qt.Key_Tab)
         self.assertEqual(self.qpart.text, 'aaaaa\naaaaaXXXXX\naaaaaXXXXX')
 
+    @base.in_main_loop
     def test_manual(self):
         self._window.show()
 
         self.qpart.text = 'aaaaa\naaaaaXXXXX\n'
 
-        base._processPendingEvents(self.app)
+        QTest.qWait(1)
 
         self.qpart.cursorPosition = (2, 0)
         QTest.keyClicks(self.qpart, "a")
@@ -97,14 +99,16 @@ def test_too_long_list(self):
 
         self.qpart.text = '\n'.join(['asdf' + str(i) \
                                         for i in range(100)]) + '\n'
-        base._processPendingEvents(self.app)
+
+        QTest.qWait(1)
+
         self.qpart.cursorPosition = (100, 0)
         QTest.keyClicks(self.qpart, "asdf")
         self.assertIsNotNone(self.qpart._completer._widget)
 
         self.qpart.text = '\n'.join(['asdf' + str(i) \
                                         for i in range(1000)]) + '\n'
-        base._processPendingEvents(self.app)
+        QTest.qWait(1)
         self.qpart.cursorPosition = (1000, 0)
         QTest.keyClicks(self.qpart, "asdf")
         self.assertIsNone(self.qpart._completer._widget)
diff --git a/tests/test_draw_whitespace.py b/tests/test_draw_whitespace.py
index fa484e9..88c92c3 100755
--- a/tests/test_draw_whitespace.py
+++ b/tests/test_draw_whitespace.py
@@ -8,8 +8,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 
diff --git a/tests/test_edit.py b/tests/test_edit.py
index 0f91f9c..3e55db4 100755
--- a/tests/test_edit.py
+++ b/tests/test_edit.py
@@ -5,8 +5,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 
diff --git a/tests/test_indent.py b/tests/test_indent.py
index 2ce5d57..9e44763 100755
--- a/tests/test_indent.py
+++ b/tests/test_indent.py
@@ -5,8 +5,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 
diff --git a/tests/test_indenter/indenttest.py b/tests/test_indenter/indenttest.py
index 58e7a53..0fd0b8b 100644
--- a/tests/test_indenter/indenttest.py
+++ b/tests/test_indenter/indenttest.py
@@ -3,8 +3,8 @@
 import sip
 sip.setapi('QString', 2)
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 import sys
 import os
diff --git a/tests/test_rectangular_selection.py b/tests/test_rectangular_selection.py
index c814ec0..e18965e 100755
--- a/tests/test_rectangular_selection.py
+++ b/tests/test_rectangular_selection.py
@@ -8,8 +8,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 
diff --git a/tests/test_vim.py b/tests/test_vim.py
index 2f27fe3..24b5df6 100755
--- a/tests/test_vim.py
+++ b/tests/test_vim.py
@@ -6,8 +6,8 @@
 
 import base
 
-from PyQt4.QtCore import Qt
-from PyQt4.QtTest import QTest
+from PyQt5.QtCore import Qt
+from PyQt5.QtTest import QTest
 
 from qutepart import Qutepart
 

From 8e8ac3fee4252b7aff5c9401dedc1119235ac973 Mon Sep 17 00:00:00 2001
From: Andrei Kopats <hlamer@tut.by>
Date: Thu, 13 Aug 2015 14:59:07 +0300
Subject: [PATCH 3/4] qt5: vim test fix

---
 qutepart/vim.py   | 5 ++---
 tests/test_vim.py | 2 --
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/qutepart/vim.py b/qutepart/vim.py
index fd6f441..a84154a 100644
--- a/qutepart/vim.py
+++ b/qutepart/vim.py
@@ -652,11 +652,10 @@ def cmdJoinLines(self, cmd, repeatLineCount=None):
 
         self._qpart.setTextCursor(cursor)
 
-
     def cmdAppendAfterChar(self, cmd):
         cursor = self._qpart.textCursor()
-        cursor.movePosition(QTextCursor.Right)
-        self._qpart.setTextCursor(cursor)
+        cursor.setPosition(cursor.position() + 1)
+        self._qpart.setTextCursor(cursor)  # move Right doesn't work here on Py3 Qt5
         self.switchMode(Insert)
 
     def cmdReplaceSelectedLines(self, cmd):
diff --git a/tests/test_vim.py b/tests/test_vim.py
index 24b5df6..90a5de0 100755
--- a/tests/test_vim.py
+++ b/tests/test_vim.py
@@ -736,8 +736,6 @@ def test_08(self):
                          'The quick The quick brown fox')
 
 
-
-
 class Visual(_Test):
     def test_01(self):
         """ x

From 0e9612914f0ab90fd62c8160590191bfe4153658 Mon Sep 17 00:00:00 2001
From: Andrei Kopats <hlamer@tut.by>
Date: Tue, 20 Oct 2015 21:03:43 +0300
Subject: [PATCH 4/4] py3: fix setup.py

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 5b26ba1..dcac71e 100755
--- a/setup.py
+++ b/setup.py
@@ -83,7 +83,7 @@ def _checkDependencies():
                                  libraries = ['pcre'],
                                  include_dirs=include_dirs,
                                  library_dirs=library_dirs):
-        print("Failed to find pcre library."
+        print("Failed to find pcre library.")
         print("Try to install libpcre{version}-dev package, or go to http://pcre.org")
         print("If not standard directories are used, pass parameters:")
         print("\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build")