Repository: spyder-ide/spyder

Issue:
Find Russian symbols with a regexp gives an error
 
<!---   *** BEFORE SUBMITTING: PASTE CLIPBOARD HERE TO COMPLETE YOUR REPORT ***   ---!>
<!--- **PLEASE READ:** Before submitting here, please carefully consult our *Troubleshooting Guide*: https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ and search the issues page for your error/problem, as most posted bugs are duplicates or easy fixes.

If you don't find anything, please provide a detailed step-by-step description (in English) of the problem and what led up to it below. Issue reports without a clear way to reproduce them will be closed. Thanks! --->

## Problem Description

### Steps:

<!--- You can use Markdown here --->

* press **Ctrl + R**
* enter **Russian** symbol in first text field
* catch an **Error**

### Traceback
```python-traceback
  File "/home/dmitry.makarevich/anaconda3/envs/py2/lib/python2.7/site-packages/spyder/widgets/findreplace.py", line 368, in text_has_been_edited
    self.find(changed=True, forward=True, start_highlight_timer=True)
  File "/home/dmitry.makarevich/anaconda3/envs/py2/lib/python2.7/site-packages/spyder/widgets/findreplace.py", line 416, in find
    words=words, regexp=regexp)
  File "/home/dmitry.makarevich/anaconda3/envs/py2/lib/python2.7/site-packages/spyder/widgets/mixins.py", line 497, in find_text
    pattern = QRegularExpression(r"\b{}\b".format(text) if words else
UnicodeEncodeError: 'ascii' codec can't encode character u'\u044b' in position 1: ordinal not in range(128)
```

## Package Versions

* Spyder version: 3.2.8 
* Python version: 2.7.14
* Qt version: 5.6.2
* PyQt5 version: 5.6
* Operating system: Linux 4.4.0-116-generic

### Dependencies

```
pyflakes >=0.5.0  :  1.6.0 (OK)
pycodestyle >=2.3 :  2.3.1 (OK)
pygments >=2.0    :  2.2.0 (OK)
pandas >=0.13.1   :  0.22.0 (OK)
numpy >=1.7       :  1.14.2 (OK)
sphinx >=0.6.6    :  1.7.1 (OK)
rope >=0.9.4      :  0.10.7 (OK)
jedi >=0.9.0      :  0.11.1 (OK)
psutil >=0.3      :  5.4.3 (OK)
nbconvert >=4.0   :  5.3.1 (OK)
sympy >=0.7.3     :  None (NOK)
cython >=0.21     :  None (NOK)
qtconsole >=4.2.0 :  4.3.1 (OK)
IPython >=4.0;<6.0:  5.4.1 (OK)
pylint >=0.25     :  1.8.2 (OK)
```



Discussion:
Thanks for reporting. We'll fix this in our next version (3.2.9).

Resolving patch:
diff --git a/spyder/widgets/mixins.py b/spyder/widgets/mixins.py
--- a/spyder/widgets/mixins.py
+++ b/spyder/widgets/mixins.py
@@ -475,10 +475,13 @@ def find_text(self, text, changed=True, forward=True, case=False,
         """Find text"""
         cursor = self.textCursor()
         findflag = QTextDocument.FindFlag()
+
         if not forward:
             findflag = findflag | QTextDocument.FindBackward
+
         if case:
             findflag = findflag | QTextDocument.FindCaseSensitively
+
         moves = [QTextCursor.NoMove]
         if forward:
             moves += [QTextCursor.NextWord, QTextCursor.Start]
@@ -491,16 +494,20 @@ def find_text(self, text, changed=True, forward=True, case=False,
                     cursor.movePosition(QTextCursor.PreviousWord)
         else:
             moves += [QTextCursor.End]
-        if not regexp:
+
+        if regexp:
+            text = to_text_string(text)
+        else:
             text = re.escape(to_text_string(text))
+
         if QT55_VERSION:
-            pattern = QRegularExpression(r"\b{}\b".format(text) if words else
+            pattern = QRegularExpression(u"\\b{}\\b".format(text) if words else
                                          text)
             if case:
                 pattern.setPatternOptions(
                     QRegularExpression.CaseInsensitiveOption)
         else:
-            pattern = QRegExp(r"\b{}\b".format(text)
+            pattern = QRegExp(u"\\b{}\\b".format(text)
                               if words else text, Qt.CaseSensitive if case else
                               Qt.CaseInsensitive, QRegExp.RegExp2)
 
@@ -517,6 +524,7 @@ def find_text(self, text, changed=True, forward=True, case=False,
             if found_cursor is not None and not found_cursor.isNull():
                 self.setTextCursor(found_cursor)
                 return True
+
         return False
 
     def is_editor(self):
@@ -529,10 +537,13 @@ def get_number_matches(self, pattern, source_text='', case=False,
         pattern = to_text_string(pattern)
         if not pattern:
             return 0
+
         if not regexp:
             pattern = re.escape(pattern)
+
         if not source_text:
             source_text = to_text_string(self.toPlainText())
+
         try:
             if case:
                 regobj = re.compile(pattern)


Test patch:
diff --git a/spyder/widgets/tests/test_mixins.py b/spyder/widgets/tests/test_mixins.py
--- a/spyder/widgets/tests/test_mixins.py
+++ b/spyder/widgets/tests/test_mixins.py
@@ -16,6 +16,7 @@
 class BaseWidget(QPlainTextEdit, mixins.BaseEditMixin):
     pass
 
+
 # --- Fixtures
 # -----------------------------------------------------------------------------
 
@@ -29,6 +30,25 @@ def mixinsbot(qtbot):
 
 # --- Tests
 # -----------------------------------------------------------------------------
+def test_get_unicode_regexp(mixinsbot):
+    """
+    Test that we can search with regexp's containing unicode
+    characters.
+
+    For issue 6812
+    """
+    qtbot, widget = mixinsbot
+    get = widget.get_number_matches
+
+    # CodeEditor and findreplace texts are handled in
+    # unicode by PyQt5 in Python 2
+    code = (u'print("И")\n'
+            u'foo("И")')
+    widget.setPlainText(code)
+    cursor = widget.textCursor()
+    cursor.setPosition(widget.get_position('sof'))
+    assert widget.find_text(u't.*И', regexp=True)
+    assert get(u't.*И', source_text=code, regexp=True) == 1
 
 
 def test_get_number_matches(mixinsbot):