Example #1
0
    def silent_exec_method(self, code):
        """Silently execute a kernel method and save its reply

        The methods passed here **don't** involve getting the value
        of a variable but instead replies that can be handled by
        ast.literal_eval.

        To get a value see `get_value`

        Parameters
        ----------
        code : string
            Code that contains the kernel method as part of its
            string

        See Also
        --------
        handle_exec_method : Method that deals with the reply

        Note
        ----
        This is based on the _silent_exec_callback method of
        RichJupyterWidget. Therefore this is licensed BSD
        """
        # Generate uuid, which would be used as an indication of whether or
        # not the unique request originated from here
        local_uuid = to_text_string(uuid.uuid1())
        code = to_text_string(code)
        msg_id = self.kernel_client.execute("", silent=True, user_expressions={local_uuid: code})
        self._kernel_methods[local_uuid] = code
        self._request_info["execute"][msg_id] = self._ExecutionRequest(msg_id, "silent_exec_method")
Example #2
0
 def setup(self):
     iofuncs = self.get_internal_funcs()+self.get_3rd_party_funcs()
     load_extensions = {}
     save_extensions = {}
     load_funcs = {}
     save_funcs = {}
     load_filters = []
     save_filters = []
     load_ext = []
     for ext, name, loadfunc, savefunc in iofuncs:
         filter_str = to_text_string(name + " (*%s)" % ext)
         if loadfunc is not None:
             load_filters.append(filter_str)
             load_extensions[filter_str] = ext
             load_funcs[ext] = loadfunc
             load_ext.append(ext)
         if savefunc is not None:
             save_extensions[filter_str] = ext
             save_filters.append(filter_str)
             save_funcs[ext] = savefunc
     load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\
                                           " *".join(load_ext)+")"))
     load_filters.append(to_text_string(_("All files (*.*)")))
     self.load_filters = "\n".join(load_filters)
     self.save_filters = "\n".join(save_filters)
     self.load_funcs = load_funcs
     self.save_funcs = save_funcs
     self.load_extensions = load_extensions
     self.save_extensions = save_extensions
Example #3
0
def test_recent_projects_menu_action(projects, tmpdir):
    """
    Test that the actions of the submenu 'Recent Projects' in the 'Projects'
    main menu are working as expected.

    Regression test for Issue #8450.
    """
    recent_projects_len = len(projects.recent_projects)

    # Create the directories.
    path0 = to_text_string(tmpdir.mkdir('project0'))
    path1 = to_text_string(tmpdir.mkdir('project1'))
    path2 = to_text_string(tmpdir.mkdir('project2'))

    # Open projects in path0, path1, and path2.
    projects.open_project(path=path0)
    projects.open_project(path=path1)
    projects.open_project(path=path2)
    assert (len(projects.recent_projects_actions) ==
            recent_projects_len + 3 + 2)
    assert projects.get_active_project().root_path == path2

    # Trigger project1 in the list of Recent Projects actions.
    projects.recent_projects_actions[1].trigger()
    assert projects.get_active_project().root_path == path1

    # Trigger project0 in the list of Recent Projects actions.
    projects.recent_projects_actions[2].trigger()
    assert projects.get_active_project().root_path == path0
Example #4
0
    def goto_line(self, line, column=0, end_column=0, move=True, word=''):
        """
        Moves the text cursor to the specified position.

        :param line: Number of the line to go to (0 based)
        :param column: Optional column number. Default is 0 (start of line).
        :param move: True to move the cursor. False will return the cursor
                     without setting it on the editor.
        :param word: Highlight the word, when moving to the line.
        :return: The new text cursor
        :rtype: QtGui.QTextCursor
        """
        line = min(line, self.line_count())
        text_cursor = self._move_cursor_to(line)
        if column:
            text_cursor.movePosition(text_cursor.Right, text_cursor.MoveAnchor,
                                     column)
        if end_column:
            text_cursor.movePosition(text_cursor.Right, text_cursor.KeepAnchor,
                                     end_column)
        if move:
            block = text_cursor.block()
            self.unfold_if_colapsed(block)
            self._editor.setTextCursor(text_cursor)

            if self._editor.isVisible():
                self._editor.centerCursor()
            else:
                self._editor.focus_in.connect(
                    self._editor.center_cursor_on_next_focus)
            if word and to_text_string(word) in to_text_string(block.text()):
                self._editor.find(word, QTextDocument.FindCaseSensitively)
        return text_cursor
Example #5
0
    def run(self):
        html_text = self.html_text_no_doc
        doc = self.doc
        if doc is not None:
            if type(doc) is dict and 'docstring' in doc.keys():
                try:
                    context = generate_context(name=doc['name'],
                                               argspec=doc['argspec'],
                                               note=doc['note'],
                                               math=self.math_option,
                                               img_path=self.img_path)
                    html_text = sphinxify(doc['docstring'], context)
                    if doc['docstring'] == '':
                        html_text += '<div class="hr"></div>'
                        html_text += self.html_text_no_doc

                except Exception as error:
                    self.error_msg.emit(to_text_string(error))
                    return
            elif self.context is not None:
                try:
                    html_text = sphinxify(doc, self.context)
                except Exception as error:
                    self.error_msg.emit(to_text_string(error))
                    return
        self.html_ready.emit(html_text)
Example #6
0
    def format_measure(measure):
        """Get format and units for data coming from profiler task."""
        # Convert to a positive value.
        measure = abs(measure)

        # For number of calls
        if isinstance(measure, int):
            return to_text_string(measure)

        # For time measurements
        if 1.e-9 < measure <= 1.e-6:
            measure = u"{0:.2f} ns".format(measure / 1.e-9)
        elif 1.e-6 < measure <= 1.e-3:
            measure = u"{0:.2f} us".format(measure / 1.e-6)
        elif 1.e-3 < measure <= 1:
            measure = u"{0:.2f} ms".format(measure / 1.e-3)
        elif 1 < measure <= 60:
            measure = u"{0:.2f} sec".format(measure)
        elif 60 < measure <= 3600:
            m, s = divmod(measure, 3600)
            if s > 60:
                m, s = divmod(measure, 60)
                s = to_text_string(s).split(".")[-1]
            measure = u"{0:.0f}.{1:.2s} min".format(m, s)
        else:
            h, m = divmod(measure, 3600)
            if m > 60:
                m /= 60
            measure = u"{0:.0f}h:{1:.0f}min".format(h, m)
        return measure
Example #7
0
 def data(self, index, role=Qt.DisplayRole):
     """Cell content"""
     if not index.isValid():
         return to_qvariant()
     if role == Qt.DisplayRole or role == Qt.EditRole:
         column = index.column()
         row = index.row()
         if column == 0:
             return to_qvariant(to_text_string(self.df_index[row]))
         else:
             value = self.get_value(row, column-1)
             if isinstance(value, float):
                 try:
                     return to_qvariant(self._format % value)
                 except (ValueError, TypeError):
                     # may happen if format = '%d' and value = NaN;
                     # see issue 4139
                     return to_qvariant(DEFAULT_FORMAT % value)
             else:
                 try:
                     return to_qvariant(to_text_string(value))
                 except UnicodeDecodeError:
                     return to_qvariant(encoding.to_unicode(value))
     elif role == Qt.BackgroundColorRole:
         return to_qvariant(self.get_bgcolor(index))
     elif role == Qt.FontRole:
         return to_qvariant(get_font(font_size_delta=DEFAULT_SMALL_DELTA))
     return to_qvariant()
Example #8
0
 def save_to_conf(self):
     """Save settings to configuration file"""
     for checkbox, (option, _default) in list(self.checkboxes.items()):
         self.set_option(option, checkbox.isChecked())
     for radiobutton, (option, _default) in list(self.radiobuttons.items()):
         self.set_option(option, radiobutton.isChecked())
     for lineedit, (option, _default) in list(self.lineedits.items()):
         self.set_option(option, to_text_string(lineedit.text()))
     for textedit, (option, _default) in list(self.textedits.items()):
         self.set_option(option, to_text_string(textedit.toPlainText()))
     for spinbox, (option, _default) in list(self.spinboxes.items()):
         self.set_option(option, spinbox.value())
     for combobox, (option, _default) in list(self.comboboxes.items()):
         data = combobox.itemData(combobox.currentIndex())
         self.set_option(option, from_qvariant(data, to_text_string))
     for (fontbox, sizebox), option in list(self.fontboxes.items()):
         font = fontbox.currentFont()
         font.setPointSize(sizebox.value())
         self.set_font(font, option)
     for clayout, (option, _default) in list(self.coloredits.items()):
         self.set_option(option, to_text_string(clayout.lineedit.text()))
     for (clayout, cb_bold, cb_italic), (option, _default) in list(self.scedits.items()):
         color = to_text_string(clayout.lineedit.text())
         bold = cb_bold.isChecked()
         italic = cb_italic.isChecked()
         self.set_option(option, (color, bold, italic))
Example #9
0
    def update_browse_tabs_menu(self):
        """Update browse tabs menu"""
        self.browse_tabs_menu.clear()
        names = []
        dirnames = []
        for index in range(self.count()):
            if self.menu_use_tooltips:
                text = to_text_string(self.tabToolTip(index))
            else:
                text = to_text_string(self.tabText(index))
            names.append(text)
            if osp.isfile(text):
                # Testing if tab names are filenames
                dirnames.append(osp.dirname(text))
        offset = None
        
        # If tab names are all filenames, removing common path:
        if len(names) == len(dirnames):
            common = get_common_path(dirnames)
            if common is None:
                offset = None
            else:
                offset = len(common)+1
                if offset <= 3:
                    # Common path is not a path but a drive letter...
                    offset = None

        for index, text in enumerate(names):
            tab_action = create_action(self, text[offset:],
                                       icon=self.tabIcon(index),
                                       toggled=lambda state, index=index:
                                               self.setCurrentIndex(index),
                                       tip=self.tabToolTip(index))
            tab_action.setChecked(index == self.currentIndex())
            self.browse_tabs_menu.addAction(tab_action)
Example #10
0
 def setup_and_check(self, value):
     """Verify if TextEditor is able to display strings passed to it."""
     try:
         to_text_string(value, 'utf8')
         return True
     except:
         return False
Example #11
0
def create_folders_files(tmpdir, request):
    """A project directory with dirs and files for testing."""
    project_dir = to_text_string(tmpdir.mkdir('project'))
    destination_dir = to_text_string(tmpdir.mkdir('destination'))
    top_folder = osp.join(project_dir, 'top_folder_in_proj')
    if not osp.exists(top_folder):
        os.mkdir(top_folder)
    list_paths = []
    for item in request.param:
        if osp.splitext(item)[1]:
            if osp.split(item)[0]:
                dirs, fname = osp.split(item)
                dirpath = osp.join(top_folder, dirs)
                if not osp.exists(dirpath):
                    os.makedirs(dirpath)
                    item_path = osp.join(dirpath, fname)
            else:
                item_path = osp.join(top_folder, item)
        else:
            dirpath = osp.join(top_folder, item)
            if not osp.exists(dirpath):
                os.makedirs(dirpath)
                item_path = dirpath
        if not osp.isdir(item_path):
            with open(item_path, 'w') as fh:
                fh.write("File Path:\n" + str(item_path).replace(os.sep, '/'))
        list_paths.append(item_path)
    return list_paths, project_dir, destination_dir, top_folder
Example #12
0
def test_fix_indentation(code_editor_indent_bot):
    """Test fix_indentation() method."""
    editor, qtbot = code_editor_indent_bot
    # Contains tabs.
    original = ("\t\n"
                "class a():\t\n"
                "\tself.b = 1\n"
                "\tprint(self.b)\n"
                "\n"
                )
    # Fix indentation replaces tabs with indent_chars spaces.
    fixed = ("  \n"
             "class a():  \n"
             "  self.b = 1\n"
             "  print(self.b)\n"
             "\n"
             )
    editor.set_text(original)
    editor.fix_indentation()
    assert to_text_string(editor.toPlainText()) == fixed
    assert editor.document().isModified()
    # Test that undo/redo works - issue 1754.
    editor.undo()
    assert to_text_string(editor.toPlainText()) == original
    assert not editor.document().isModified()
    editor.redo()
    assert to_text_string(editor.toPlainText()) == fixed
    assert editor.document().isModified()
Example #13
0
 def chdir(self, directory=None, browsing_history=False):
     """Set directory as working directory"""
     if directory is not None:
         directory = osp.abspath(to_text_string(directory))
     if browsing_history:
         directory = self.history[self.histindex]
     elif directory in self.history:
         self.histindex = self.history.index(directory)
     else:
         if self.histindex is None:
             self.history = []
         else:
             self.history = self.history[:self.histindex+1]
         if len(self.history) == 0 or \
            (self.history and self.history[-1] != directory):
             self.history.append(directory)
         self.histindex = len(self.history)-1
     directory = to_text_string(directory)
     if PY2:
         PermissionError = OSError
     try:
         os.chdir(directory)
         self.parent_widget.open_dir.emit(directory)
         self.refresh(new_path=directory, force_current=True)
     except PermissionError:
         QMessageBox.critical(self.parent_widget, "Error",
                              _("You don't have the right permissions to "
                                "open this directory"))
Example #14
0
 def __repr__(self):
     match = to_text_string(self.match).rstrip()
     font = get_font()
     _str = to_text_string("<b>{1}</b> ({2}): "
                           "<span style='font-family:{0};"
                           "font-size:75%;'>{3}</span>")
     return _str.format(font.family(), self.lineno, self.colno, match)
Example #15
0
 def create_new_folder(self, current_path, title, subtitle, is_package):
     """Create new folder"""
     if current_path is None:
         current_path = ''
     if osp.isfile(current_path):
         current_path = osp.dirname(current_path)
     name, valid = QInputDialog.getText(self, title, subtitle,
                                        QLineEdit.Normal, "")
     if valid:
         dirname = osp.join(current_path, to_text_string(name))
         try:
             os.mkdir(dirname)
         except EnvironmentError as error:
             QMessageBox.critical(self, title,
                                  _("<b>Unable "
                                    "to create folder <i>%s</i></b>"
                                    "<br><br>Error message:<br>%s"
                                    ) % (dirname, to_text_string(error)))
         finally:
             if is_package:
                 fname = osp.join(dirname, '__init__.py')
                 try:
                     with open(fname, 'wb') as f:
                         f.write(to_binary_string('#'))
                     return dirname
                 except EnvironmentError as error:
                     QMessageBox.critical(self, title,
                                          _("<b>Unable "
                                            "to create file <i>%s</i></b>"
                                            "<br><br>Error message:<br>%s"
                                            ) % (fname,
                                                 to_text_string(error)))
Example #16
0
 def run(self):
     html_text = self.html_text_no_doc
     doc = self.doc
     if doc is not None:
         if type(doc) is dict and 'docstring' in doc.keys():
             try:
                 context = generate_context(name=doc['name'],
                                            argspec=doc['argspec'],
                                            note=doc['note'],
                                            math=self.math_option,
                                            img_path=self.img_path,
                                            css_path=self.css_path)
                 html_text = sphinxify(doc['docstring'], context)
                 if doc['docstring'] == '':
                     if any([doc['name'], doc['argspec'], doc['note']]):
                         msg = _("No further documentation available")
                         html_text += '<div class="hr"></div>'
                     else:
                         msg = _("No documentation available")
                     html_text += '<div id="doc-warning">%s</div>' % msg
             except Exception as error:
                 self.error_msg.emit(to_text_string(error))
                 return
         elif self.context is not None:
             try:
                 html_text = sphinxify(doc, self.context)
             except Exception as error:
                 self.error_msg.emit(to_text_string(error))
                 return
     self.html_ready.emit(html_text)
Example #17
0
    def get_number_matches(self, pattern, source_text='', case=False,
                           regexp=False):
        """Get the number of matches for the searched text."""
        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)
            else:
                regobj = re.compile(pattern, re.IGNORECASE)
        except sre_constants.error:
            return None

        number_matches = 0
        for match in regobj.finditer(source_text):
            number_matches += 1

        return number_matches
Example #18
0
    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.add_dockwidget(self)
        self.edit.connect(self.main.editor.load)
        self.removed.connect(self.main.editor.removed)
        self.removed_tree.connect(self.main.editor.removed_tree)
        self.renamed.connect(self.main.editor.renamed)
        self.main.editor.open_dir.connect(self.chdir)
        self.create_module.connect(self.main.editor.new)
        self.run.connect(
                     lambda fname:
                     self.main.open_external_console(to_text_string(fname),
                                         osp.dirname(to_text_string(fname)),
                                         '', False, False, True, '', False))
        # Signal "set_explorer_cwd(QString)" will refresh only the
        # contents of path passed by the signal in explorer:
        self.main.workingdirectory.set_explorer_cwd.connect(
                     lambda directory: self.refresh_plugin(new_path=directory,
                                                           force_current=True))
        self.open_dir.connect(
                     lambda dirname:
                     self.main.workingdirectory.chdir(dirname,
                                                      refresh_explorer=False))

        self.sig_open_file.connect(self.main.open_file)
        self.sig_new_file.connect(lambda t: self.main.editor.new(text=t))
Example #19
0
 def find_multiline_pattern(self, regexp, cursor, findflag):
     """Reimplement QTextDocument's find method
     
     Add support for *multiline* regular expressions"""
     pattern = to_text_string(regexp.pattern())
     text = to_text_string(self.toPlainText())
     try:
         regobj = re.compile(pattern)
     except sre_constants.error:
         return
     if findflag & QTextDocument.FindBackward:
         # Find backward
         offset = min([cursor.selectionEnd(), cursor.selectionStart()])
         text = text[:offset]
         matches = [_m for _m in regobj.finditer(text, 0, offset)]
         if matches:
             match = matches[-1]
         else:
             return
     else:
         # Find forward
         offset = max([cursor.selectionEnd(), cursor.selectionStart()])
         match = regobj.search(text, offset)
     if match:
         pos1, pos2 = match.span()
         fcursor = self.textCursor()
         fcursor.setPosition(pos1)
         fcursor.setPosition(pos2, QTextCursor.KeepAnchor)
         return fcursor
Example #20
0
    def get_options(self, all=False):
        # Getting options
        self.search_text.lineEdit().setStyleSheet("")
        self.exclude_pattern.lineEdit().setStyleSheet("")

        utext = to_text_string(self.search_text.currentText())
        if not utext:
            return
        try:
            texts = [(utext.encode('utf-8'), 'utf-8')]
        except UnicodeEncodeError:
            texts = []
            for enc in self.supported_encodings:
                try:
                    texts.append((utext.encode(enc), enc))
                except UnicodeDecodeError:
                    pass
        text_re = self.edit_regexp.isChecked()
        exclude = to_text_string(self.exclude_pattern.currentText())
        exclude_re = self.exclude_regexp.isChecked()
        case_sensitive = self.case_button.isChecked()
        python_path = False

        if not case_sensitive:
            texts = [(text[0].lower(), text[1]) for text in texts]

        file_search = self.path_selection_combo.is_file_search()
        path = self.path_selection_combo.get_current_searchpath()

        # Finding text occurrences
        if not exclude_re:
            exclude = fnmatch.translate(exclude)
        else:
            try:
                exclude = re.compile(exclude)
            except Exception:
                exclude_edit = self.exclude_pattern.lineEdit()
                exclude_edit.setStyleSheet(self.REGEX_INVALID)
                return None

        if text_re:
            try:
                texts = [(re.compile(x[0]), x[1]) for x in texts]
            except Exception:
                self.search_text.lineEdit().setStyleSheet(self.REGEX_INVALID)
                return None

        if all:
            search_text = [to_text_string(self.search_text.itemText(index))
                           for index in range(self.search_text.count())]
            exclude = [to_text_string(self.exclude_pattern.itemText(index))
                       for index in range(self.exclude_pattern.count())]
            path_history = self.path_selection_combo.get_external_paths()
            exclude_idx = self.exclude_pattern.currentIndex()
            more_options = self.more_options.isChecked()
            return (search_text, text_re, [],
                    exclude, exclude_idx, exclude_re,
                    python_path, more_options, case_sensitive, path_history)
        else:
            return (path, file_search, exclude, texts, text_re, case_sensitive)
Example #21
0
 def _find_parenthesis(self, position, forward=True):
     """ If 'forward' is True (resp. False), proceed forwards
         (resp. backwards) through the line that contains 'position' until an
         unmatched closing (resp. opening) parenthesis is found. Returns a
         tuple containing the position of this parenthesis (or -1 if it is
         not found) and the number commas (at depth 0) found along the way.
     """
     commas = depth = 0
     document = self._text_edit.document()
     char = to_text_string(document.characterAt(position))
     # Search until a match is found or a non-printable character is
     # encountered.
     while category(char) != 'Cc' and position > 0:
         if char == ',' and depth == 0:
             commas += 1
         elif char == ')':
             if forward and depth == 0:
                 break
             depth += 1
         elif char == '(':
             if not forward and depth == 0:
                 break
             depth -= 1
         position += 1 if forward else -1
         char = to_text_string(document.characterAt(position))
     else:
         position = -1
     return position, commas
Example #22
0
 def rename_file(self, fname):
     """Rename file"""
     path, valid = QInputDialog.getText(self, _('Rename'),
                           _('New name:'), QLineEdit.Normal,
                           osp.basename(fname))
     if valid:
         path = osp.join(osp.dirname(fname), to_text_string(path))
         if path == fname:
             return
         if osp.exists(path):
             if QMessageBox.warning(self, _("Rename"),
                      _("Do you really want to rename <b>%s</b> and "
                        "overwrite the existing file <b>%s</b>?"
                        ) % (osp.basename(fname), osp.basename(path)),
                      QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
                 return
         try:
             misc.rename_file(fname, path)
             self.parent_widget.renamed.emit(fname, path)
             return path
         except EnvironmentError as error:
             QMessageBox.critical(self, _("Rename"),
                         _("<b>Unable to rename file <i>%s</i></b>"
                           "<br><br>Error message:<br>%s"
                           ) % (osp.basename(fname), to_text_string(error)))
Example #23
0
    def get_number_matches(self, pattern, source_text='', case=False):
        """Get the number of matches for the searched text."""
        pattern = to_text_string(pattern)
        if not pattern:
            return 0
        if not source_text:
            if WEBENGINE:
                self.page().toPlainText(self.set_source_text)
                source_text = to_text_string(self.source_text)
            else:
                source_text = to_text_string(
                        self.page().mainFrame().toPlainText())
        try:
            if case:
                regobj = re.compile(pattern)
            else:
                regobj = re.compile(pattern, re.IGNORECASE)
        except sre_constants.error:
            return

        number_matches = 0
        for match in regobj.finditer(source_text):
            number_matches += 1

        return number_matches
Example #24
0
 def update_btn_state(self):
     user = to_text_string(self.le_user.text()).strip() != ''
     password = to_text_string(self.le_password.text()).strip() != ''
     token = to_text_string(self.le_token.text()).strip() != ''
     enable = ((user and password and
               self.tabs.currentIndex() == 0) or
               (token and self.tabs.currentIndex() == 1))
     self.bt_sign_in.setEnabled(enable)
Example #25
0
 def select_directory(self):
     """Select directory"""
     self.redirect_stdio.emit(False)
     directory = getexistingdirectory(self, _("Select directory"),
                                      self.path)
     if directory:
         directory = to_text_string(osp.abspath(to_text_string(directory)))
     self.redirect_stdio.emit(True)
     return directory
    def start(self, wdir=None, args=None, pythonpath=None):
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)
        print(filename)
        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            filename = osp.normpath(filename).replace(os.sep, '/')
        if args is None:
            args = self._last_args
            if args is None:
                args = []
        if pythonpath is None:
            pythonpath = self._last_pythonpath
        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Running tests, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(filename)
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(self.finished)
        self.stop_button.clicked.connect(self.process.kill)

        if pythonpath is not None:
            env = [to_text_string(_pth)
                   for _pth in self.process.systemEnvironment()]
            baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath)
            self.process.setEnvironment(env)

        self.output = ''
        self.error_output = ''

        executable = "py.test"
        p_args = ['--junit-xml', self.DATAPATH]

#        executable = "nosetests"
#        p_args = ['--with-xunit', "--xunit-file=%s" % self.DATAPATH]

        if args:
            p_args.extend(programs.shell_split(args))
        self.process.start(executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
Example #27
0
    def find_text(self, text, changed=True, forward=True, case=False,
                  words=False, regexp=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]
            if changed:
                if to_text_string(cursor.selectedText()):
                    new_position = min([cursor.selectionStart(),
                                        cursor.selectionEnd()])
                    cursor.setPosition(new_position)
                else:
                    cursor.movePosition(QTextCursor.PreviousWord)
        else:
            moves += [QTextCursor.End]

        if regexp:
            text = to_text_string(text)
        else:
            text = re.escape(to_text_string(text))

        if QT55_VERSION:
            pattern = QRegularExpression(u"\\b{}\\b".format(text) if words else
                                         text)
            if case:
                pattern.setPatternOptions(
                    QRegularExpression.CaseInsensitiveOption)
        else:
            pattern = QRegExp(u"\\b{}\\b".format(text)
                              if words else text, Qt.CaseSensitive if case else
                              Qt.CaseInsensitive, QRegExp.RegExp2)

        for move in moves:
            cursor.movePosition(move)
            if regexp and '\\n' in text:
                # Multiline regular expression
                found_cursor = self.find_multiline_pattern(pattern, cursor,
                                                           findflag)
            else:
                # Single line find: using the QTextDocument's find function,
                # probably much more efficient than ours
                found_cursor = self.document().find(pattern, cursor, findflag)
            if found_cursor is not None and not found_cursor.isNull():
                self.setTextCursor(found_cursor)
                return True

        return False
Example #28
0
 def save_breakpoints(self):
     breakpoints = repr(self.breakpoints)
     filename = to_text_string(self.filename)
     breakpoints = to_text_string(breakpoints)
     filename = osp.normpath(osp.abspath(filename))
     if breakpoints:
         breakpoints = eval(breakpoints)
     else:
         breakpoints = []
     save_breakpoints(filename, breakpoints)
     self.editor.sig_breakpoints_saved.emit()
Example #29
0
 def append_to_history(self, filename, command):
     """
     Append an entry to history filename
     Slot for append_to_history signal emitted by shell instance
     """
     if not is_text_string(filename):  # filename is a QString
         filename = to_text_string(filename.toUtf8(), "utf-8")
     command = to_text_string(command)
     index = self.filenames.index(filename)
     self.editors[index].append(command)
     if self.get_option("go_to_eof"):
         self.editors[index].set_cursor_position("eof")
     self.tabwidget.setCurrentIndex(index)
Example #30
0
 def get(self):
     self.runconf.args_enabled = self.clo_cb.isChecked()
     self.runconf.args = to_text_string(self.clo_edit.text())
     self.runconf.wdir_enabled = self.wd_cb.isChecked()
     self.runconf.wdir = to_text_string(self.wd_edit.text())
     self.runconf.current = self.current_radio.isChecked()
     self.runconf.systerm = self.systerm_radio.isChecked()
     self.runconf.interact = self.interact_cb.isChecked()
     self.runconf.show_kill_warning = self.show_kill_warning_cb.isChecked()
     self.runconf.post_mortem = self.post_mortem_cb.isChecked()
     self.runconf.python_args_enabled = self.pclo_cb.isChecked()
     self.runconf.python_args = to_text_string(self.pclo_edit.text())
     return self.runconf.get()
Example #31
0
File: base.py Project: zcatt/spyder
    def __move_line_or_selection(self, after_current_line=True):
        """Move current line or selected text"""
        cursor = self.textCursor()
        cursor.beginEditBlock()
        start_pos, end_pos = self.__save_selection()
        last_line = False

        # ------ Select text

        # Get selection start location
        cursor.setPosition(start_pos)
        cursor.movePosition(QTextCursor.StartOfBlock)
        start_pos = cursor.position()

        # Get selection end location
        cursor.setPosition(end_pos)
        if not cursor.atBlockStart() or end_pos == start_pos:
            cursor.movePosition(QTextCursor.EndOfBlock)
            cursor.movePosition(QTextCursor.NextBlock)
        end_pos = cursor.position()

        # Check if selection ends on the last line of the document
        if cursor.atEnd():
            if not cursor.atBlockStart() or end_pos == start_pos:
                last_line = True

        # ------ Stop if at document boundary

        cursor.setPosition(start_pos)
        if cursor.atStart() and not after_current_line:
            # Stop if selection is already at top of the file while moving up
            cursor.endEditBlock()
            self.setTextCursor(cursor)
            self.__restore_selection(start_pos, end_pos)
            return

        cursor.setPosition(end_pos, QTextCursor.KeepAnchor)
        if last_line and after_current_line:
            # Stop if selection is already at end of the file while moving down
            cursor.endEditBlock()
            self.setTextCursor(cursor)
            self.__restore_selection(start_pos, end_pos)
            return

        # ------ Move text

        sel_text = to_text_string(cursor.selectedText())
        cursor.removeSelectedText()

        if after_current_line:
            # Shift selection down
            text = to_text_string(cursor.block().text())
            sel_text = os.linesep + sel_text[0:-1]  # Move linesep at the start
            cursor.movePosition(QTextCursor.EndOfBlock)
            start_pos += len(text) + 1
            end_pos += len(text)
            if not cursor.atEnd():
                end_pos += 1
        else:
            # Shift selection up
            if last_line:
                # Remove the last linesep and add it to the selected text
                cursor.deletePreviousChar()
                sel_text = sel_text + os.linesep
                cursor.movePosition(QTextCursor.StartOfBlock)
                end_pos += 1
            else:
                cursor.movePosition(QTextCursor.PreviousBlock)
            text = to_text_string(cursor.block().text())
            start_pos -= len(text) + 1
            end_pos -= len(text) + 1

        cursor.insertText(sel_text)

        cursor.endEditBlock()
        self.setTextCursor(cursor)
        self.__restore_selection(start_pos, end_pos)
Example #32
0
        if case:
            findflag = findflag | QWebEnginePage.FindCaseSensitively

        return self.findText(text, QWebEnginePage.FindFlags(findflag))

    def get_selected_text(self):
        """Return text selected by current text cursor"""
        return self.selectedText()

    def set_source_text(self, source_text):
        """Set source text of the page. Callback for QWebEngineView."""
        self.source_text = source_text

    def get_number_matches(self, pattern, source_text='', case=False):
        """Get the number of matches for the searched text."""
        pattern = to_text_string(pattern)
        if not pattern:
            return 0
        if not source_text:
            if WEBENGINE:
                self.page().toPlainText(self.set_source_text)
                source_text = to_text_string(self.source_text)
            else:
                source_text = to_text_string(
                        self.page().mainFrame().toPlainText())
        try:
            if case:
                regobj = re.compile(pattern)
            else:
                regobj = re.compile(pattern, re.IGNORECASE)
        except sre_constants.error:
Example #33
0
def test_open_project_uses_visible_config(projects, tmpdir, value):
    """Test that when a project is opened, the project explorer is only opened
    if the config option visible_if_project_open is set."""
    projects.set_option('visible_if_project_open', value)
    projects.open_project(path=to_text_string(tmpdir))
    assert projects.dockwidget.isVisible() == value
Example #34
0
 def get_external_paths(self):
     """Returns a list of the external paths listed in the combobox."""
     return [to_text_string(self.itemText(i))
             for i in range(EXTERNAL_PATHS, self.count())]
Example #35
0
 def option_changed(self, option, value):
     """Handle when the value of an option has changed"""
     setattr(self, to_text_string(option), value)
     self.shellwidget.set_namespace_view_settings()
     if self.setup_in_progress is False:
         self.sig_option_changed.emit(option, value)
Example #36
0
    def dropEvent(self, event):
        """Reimplement Qt method"""
        event.ignore()
        action = event.dropAction()
        if action not in (Qt.MoveAction, Qt.CopyAction):
            return

        # QTreeView must not remove the source items even in MoveAction mode:
        # event.setDropAction(Qt.CopyAction)

        dst = self.get_filename(self.indexAt(event.pos()))
        yes_to_all, no_to_all = None, None
        src_list = [to_text_string(url.toString())
                    for url in event.mimeData().urls()]
        if len(src_list) > 1:
            buttons = QMessageBox.Yes|QMessageBox.YesToAll| \
                      QMessageBox.No|QMessageBox.NoToAll|QMessageBox.Cancel
        else:
            buttons = QMessageBox.Yes|QMessageBox.No
        for src in src_list:
            if src == dst:
                continue
            dst_fname = osp.join(dst, osp.basename(src))
            if osp.exists(dst_fname):
                if yes_to_all is not None or no_to_all is not None:
                    if no_to_all:
                        continue
                elif osp.isfile(dst_fname):
                    answer = QMessageBox.warning(self, _('Project explorer'),
                              _('File <b>%s</b> already exists.<br>'
                                'Do you want to overwrite it?') % dst_fname,
                              buttons)
                    if answer == QMessageBox.No:
                        continue
                    elif answer == QMessageBox.Cancel:
                        break
                    elif answer == QMessageBox.YesToAll:
                        yes_to_all = True
                    elif answer == QMessageBox.NoToAll:
                        no_to_all = True
                        continue
                else:
                    QMessageBox.critical(self, _('Project explorer'),
                                         _('Folder <b>%s</b> already exists.'
                                           ) % dst_fname, QMessageBox.Ok)
                    event.setDropAction(Qt.CopyAction)
                    return
            try:
                if action == Qt.CopyAction:
                    if osp.isfile(src):
                        shutil.copy(src, dst)
                    else:
                        shutil.copytree(src, dst)
                else:
                    if osp.isfile(src):
                        misc.move_file(src, dst)
                    else:
                        shutil.move(src, dst)
                    self.parent_widget.removed.emit(src)
            except EnvironmentError as error:
                if action == Qt.CopyAction:
                    action_str = _('copy')
                else:
                    action_str = _('move')
                QMessageBox.critical(self, _("Project Explorer"),
                                     _("<b>Unable to %s <i>%s</i></b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (action_str, src,
                                            to_text_string(error)))
Example #37
0
def test_filesystem_notifications(qtbot, projects, tmpdir):
    """
    Test that filesystem notifications are emitted when creating,
    deleting and moving files and directories.
    """
    # Create a directory for the project and some files.
    project_root = tmpdir.mkdir('project0')
    folder0 = project_root.mkdir('folder0')
    folder1 = project_root.mkdir('folder1')
    file0 = project_root.join('file0')
    file1 = folder0.join('file1')
    file2 = folder0.join('file2')
    file3 = folder1.join('file3')
    file0.write('')
    file1.write('')
    file3.write('ab')

    # Open the project
    projects.open_project(path=to_text_string(project_root))

    # Get a reference to the filesystem event handler
    fs_handler = projects.watcher.event_handler

    # Test file creation
    with qtbot.waitSignal(fs_handler.sig_file_created,
                          timeout=30000) as blocker:
        file2.write('')

    file_created, is_dir = blocker.args
    assert file_created == to_text_string(file2)
    assert not is_dir

    # Test folder creation
    with qtbot.waitSignal(fs_handler.sig_file_created,
                          timeout=3000) as blocker:
        folder2 = project_root.mkdir('folder2')

    folder_created, is_dir = blocker.args
    assert folder_created == osp.join(to_text_string(project_root), 'folder2')

    # Test file move/renaming
    new_file = osp.join(to_text_string(folder0), 'new_file')
    with qtbot.waitSignal(fs_handler.sig_file_moved, timeout=3000) as blocker:
        shutil.move(to_text_string(file1), new_file)

    original_file, file_moved, is_dir = blocker.args
    assert original_file == to_text_string(file1)
    assert file_moved == new_file
    assert not is_dir

    # Test folder move/renaming
    new_folder = osp.join(to_text_string(project_root), 'new_folder')
    with qtbot.waitSignal(fs_handler.sig_file_moved, timeout=3000) as blocker:
        shutil.move(to_text_string(folder2), new_folder)

    original_folder, folder_moved, is_dir = blocker.args
    assert original_folder == to_text_string(folder2)
    assert folder_moved == new_folder
    assert is_dir

    # Test file deletion
    with qtbot.waitSignal(fs_handler.sig_file_deleted,
                          timeout=3000) as blocker:
        os.remove(to_text_string(file0))

    deleted_file, is_dir = blocker.args
    assert deleted_file == to_text_string(file0)
    assert not is_dir
    assert not osp.exists(to_text_string(file0))

    # Test folder deletion
    with qtbot.waitSignal(fs_handler.sig_file_deleted,
                          timeout=3000) as blocker:
        shutil.rmtree(to_text_string(folder0))

    deleted_folder, is_dir = blocker.args
    assert to_text_string(folder0) in deleted_folder

    # For some reason this fails in macOS
    if not sys.platform == 'darwin':
        # Test file/folder modification
        with qtbot.waitSignal(fs_handler.sig_file_modified,
                              timeout=3000) as blocker:
            file3.write('abc')

        modified_file, is_dir = blocker.args
        assert modified_file in to_text_string(file3)
Example #38
0
 def url_to_text(self, url):
     """Convert QUrl object to displayed text in combo box"""
     return osp.splitext(to_text_string(url.path()))[0][1:]
Example #39
0
 def get_source_code(self):
     """Return associated editor source code."""
     return to_text_string(self.editor.toPlainText())
Example #40
0
    def replace_find(self, focus_replace_text=False, replace_all=False):
        """Replace and find"""
        if (self.editor is not None):
            replace_text = to_text_string(self.replace_text.currentText())
            search_text = to_text_string(self.search_text.currentText())
            re_pattern = None

            # Check regexp before proceeding
            if self.re_button.isChecked():
                try:
                    re_pattern = re.compile(search_text)
                    # Check if replace_text can be substituted in re_pattern
                    # Fixes issue #7177
                    re_pattern.sub(replace_text, '')
                except re.error:
                    # Do nothing with an invalid regexp
                    return

            case = self.case_button.isChecked()
            first = True
            cursor = None
            while True:
                if first:
                    # First found
                    seltxt = to_text_string(self.editor.get_selected_text())
                    cmptxt1 = search_text if case else search_text.lower()
                    cmptxt2 = seltxt if case else seltxt.lower()
                    if re_pattern is None:
                        has_selected = self.editor.has_selected_text()
                        if has_selected and cmptxt1 == cmptxt2:
                            # Text was already found, do nothing
                            pass
                        else:
                            if not self.find(changed=False, forward=True,
                                             rehighlight=False):
                                break
                    else:
                        if len(re_pattern.findall(cmptxt2)) > 0:
                            pass
                        else:
                            if not self.find(changed=False, forward=True,
                                             rehighlight=False):
                                break   
                    first = False
                    wrapped = False
                    position = self.editor.get_position('cursor')
                    position0 = position
                    cursor = self.editor.textCursor()
                    cursor.beginEditBlock()
                else:
                    position1 = self.editor.get_position('cursor')
                    if is_position_inf(position1,
                                       position0 + len(replace_text) -
                                       len(search_text) + 1):
                        # Identify wrapping even when the replace string
                        # includes part of the search string
                        wrapped = True
                    if wrapped:
                        if position1 == position or \
                           is_position_sup(position1, position):
                            # Avoid infinite loop: replace string includes
                            # part of the search string
                            break
                    if position1 == position0:
                        # Avoid infinite loop: single found occurrence
                        break
                    position0 = position1
                if re_pattern is None:
                    cursor.removeSelectedText()
                    cursor.insertText(replace_text)
                else:
                    seltxt = to_text_string(cursor.selectedText())
                    cursor.removeSelectedText()
                    cursor.insertText(re_pattern.sub(replace_text, seltxt))
                if self.find_next():
                    found_cursor = self.editor.textCursor()
                    cursor.setPosition(found_cursor.selectionStart(),
                                       QTextCursor.MoveAnchor)
                    cursor.setPosition(found_cursor.selectionEnd(),
                                       QTextCursor.KeepAnchor)
                else:
                    break
                if not replace_all:
                    break
            if cursor is not None:
                cursor.endEditBlock()
            if focus_replace_text:
                self.replace_text.setFocus()
Example #41
0
    def __init__(self, text, title='', font=None, parent=None, readonly=False):
        QDialog.__init__(self, parent)

        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.text = None
        self.btn_save_and_close = None

        # Display text as unicode if it comes as bytes, so users see
        # its right representation
        if is_binary_string(text):
            self.is_binary = True
            text = to_text_string(text, 'utf8')
        else:
            self.is_binary = False

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Text edit
        self.edit = QTextEdit(parent)
        self.edit.setReadOnly(readonly)
        self.edit.textChanged.connect(self.text_changed)
        self.edit.setPlainText(text)
        if font is None:
            font = get_font()
        self.edit.setFont(font)
        self.layout.addWidget(self.edit)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()
        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)

        self.layout.addLayout(btn_layout)

        # Make the dialog act as a window
        if sys.platform == 'darwin':
            # See spyder-ide/spyder#12825
            self.setWindowFlags(Qt.Tool)
        else:
            # Make the dialog act as a window
            self.setWindowFlags(Qt.Window)

        self.setWindowIcon(ima.icon('edit'))
        if title:
            try:
                unicode_title = to_text_string(title)
            except UnicodeEncodeError:
                unicode_title = u''
        else:
            unicode_title = u''

        self.setWindowTitle(_("Text editor") + \
                            u"%s" % (u" - " + unicode_title
                                     if unicode_title else u""))
Example #42
0
def value_to_display(value, minmax=False):
    """Convert value for display purpose"""
    try:
        numeric_numpy_types = (int64, int32, float64, float32,
                               complex128, complex64)
        if isinstance(value, recarray):
            fields = value.names
            display = 'Field names: ' + ', '.join(fields)
        elif isinstance(value, MaskedArray):
            display = 'Masked array'
        elif isinstance(value, ndarray):
            if minmax:
                try:
                    display = 'Min: %r\nMax: %r' % (value.min(), value.max())
                except (TypeError, ValueError):
                    display = repr(value)
            else:
                display = repr(value)
        elif isinstance(value, (list, tuple, dict, set)):
            display = CollectionsRepr.repr(value)
        elif isinstance(value, Image):
            display = '%s  Mode: %s' % (address(value), value.mode)
        elif isinstance(value, DataFrame):
            cols = value.columns
            if PY2 and len(cols) > 0:
                # Get rid of possible BOM utf-8 data present at the
                # beginning of a file, which gets attached to the first
                # column header when headers are present in the first
                # row.
                # Fixes Issue 2514
                try:
                    ini_col = to_text_string(cols[0], encoding='utf-8-sig')
                except:
                    ini_col = to_text_string(cols[0])
                cols = [ini_col] + [to_text_string(c) for c in cols[1:]]
            else:
                cols = [to_text_string(c) for c in cols]
            display = 'Column names: ' + ', '.join(list(cols))
        elif isinstance(value, NavigableString):
            # Fixes Issue 2448
            display = to_text_string(value)
        elif is_binary_string(value):
            try:
                display = to_text_string(value, 'utf8')
            except:
                display = value
        elif is_text_string(value):
            display = value
        elif isinstance(value, NUMERIC_TYPES) or isinstance(value, bool) or \
          isinstance(value, datetime.date) or \
          isinstance(value, numeric_numpy_types):
            display = repr(value)
        else:
            # Note: Don't trust on repr's. They can be inefficient and
            # so freeze Spyder quite easily
            # display = repr(value)
            type_str = to_text_string(type(value))
            display = type_str[1:-1]
    except:
        type_str = to_text_string(type(value))
        display = type_str[1:-1]

    # Truncate display at 80 chars to avoid freezing Spyder
    # because of large displays
    if len(display) > 80:
        display = display[:80].rstrip() + ' ...'

    return display
Example #43
0
 def save_history(self):
     """Save history to a text file in user home directory"""
     open(self.LOG_PATH, 'w').write("\n".join( \
             [to_text_string(self.pydocbrowser.url_combo.itemText(index))
              for index in range(self.pydocbrowser.url_combo.count())]))
Example #44
0
 def edit_script(self, filename, external_editor):
     filename = to_text_string(filename)
     if external_editor:
         self.external_editor(filename)
     else:
         self.parent().edit_script(filename)            
Example #45
0
    def replace_find(self, focus_replace_text=False, replace_all=False):
        """Replace and find."""
        if self.editor is None:
            return
        replace_text = to_text_string(self.replace_text.currentText())
        search_text = to_text_string(self.search_text.currentText())
        re_pattern = None
        case = self.case_button.isChecked()
        re_flags = re.MULTILINE if case else re.IGNORECASE | re.MULTILINE

        # Check regexp before proceeding
        if self.re_button.isChecked():
            try:
                re_pattern = re.compile(search_text, flags=re_flags)
                # Check if replace_text can be substituted in re_pattern
                # Fixes spyder-ide/spyder#7177.
                re_pattern.sub(replace_text, '')
            except re.error:
                # Do nothing with an invalid regexp
                return

        first = True
        cursor = None
        while True:
            if first:
                # First found
                seltxt = to_text_string(self.editor.get_selected_text())
                cmptxt1 = search_text if case else search_text.lower()
                cmptxt2 = seltxt if case else seltxt.lower()
                if re_pattern is None:
                    has_selected = self.editor.has_selected_text()
                    if has_selected and cmptxt1 == cmptxt2:
                        # Text was already found, do nothing
                        pass
                    else:
                        if not self.find(changed=False,
                                         forward=True,
                                         rehighlight=False):
                            break
                else:
                    if len(re_pattern.findall(cmptxt2)) > 0:
                        pass
                    else:
                        if not self.find(changed=False,
                                         forward=True,
                                         rehighlight=False):
                            break
                first = False
                wrapped = False
                position = self.editor.get_position('cursor')
                position0 = position
                cursor = self.editor.textCursor()
                cursor.beginEditBlock()
            else:
                position1 = self.editor.get_position('cursor')
                if is_position_inf(
                        position1,
                        position0 + len(replace_text) - len(search_text) + 1):
                    # Identify wrapping even when the replace string
                    # includes part of the search string
                    wrapped = True

                if wrapped:
                    if (position1 == position
                            or is_position_sup(position1, position)):
                        # Avoid infinite loop: replace string includes
                        # part of the search string
                        break

                if position1 == position0:
                    # Avoid infinite loop: single found occurrence
                    break
                position0 = position1

            if re_pattern is None:
                cursor.removeSelectedText()
                cursor.insertText(replace_text)
            else:
                seltxt = to_text_string(cursor.selectedText())

                # Note: If the selection obtained from an editor spans a line
                # break, the text will contain a Unicode U+2029 paragraph
                # separator character instead of a newline \n character.
                # See: spyder-ide/spyder#2675
                eol_char = get_eol_chars(self.editor.toPlainText())
                seltxt = seltxt.replace(u'\u2029', eol_char)

                cursor.removeSelectedText()
                cursor.insertText(re_pattern.sub(replace_text, seltxt))

            if self.find_next(set_focus=False):
                found_cursor = self.editor.textCursor()
                cursor.setPosition(found_cursor.selectionStart(),
                                   QTextCursor.MoveAnchor)
                cursor.setPosition(found_cursor.selectionEnd(),
                                   QTextCursor.KeepAnchor)
            else:
                break

            if not replace_all:
                break

        if cursor is not None:
            cursor.endEditBlock()

        if focus_replace_text:
            self.replace_text.setFocus()
        else:
            self.editor.setFocus()

        if getattr(self.editor, 'document_did_change', False):
            self.editor.document_did_change()
Example #46
0
 def is_valid(self, qstr=None):
     """Return True if string is valid"""
     if qstr is None:
         qstr = self.currentText()
     return osp.isdir(to_text_string(qstr))
Example #47
0
    def find(self,
             changed=True,
             forward=True,
             rehighlight=True,
             start_highlight_timer=False,
             multiline_replace_check=True):
        """Call the find function"""
        # When several lines are selected in the editor and replace box is
        # activated, dynamic search is deactivated to prevent changing the
        # selection. Otherwise we show matching items.
        if multiline_replace_check and self.replace_widgets[0].isVisible():
            sel_text = self.editor.get_selected_text()
            if len(to_text_string(sel_text).splitlines()) > 1:
                return None
        text = self.search_text.currentText()
        if len(text) == 0:
            self.search_text.lineEdit().setStyleSheet("")
            if not self.is_code_editor:
                # Clears the selection for WebEngine
                self.editor.find_text('')
            self.change_number_matches()
            return None
        else:
            case = self.case_button.isChecked()
            word = self.words_button.isChecked()
            regexp = self.re_button.isChecked()
            found = self.editor.find_text(text,
                                          changed,
                                          forward,
                                          case=case,
                                          word=word,
                                          regexp=regexp)

            stylesheet = self.STYLE[found]
            tooltip = self.TOOLTIP[found]
            if not found and regexp:
                error_msg = regexp_error_msg(text)
                if error_msg:  # special styling for regexp errors
                    stylesheet = self.STYLE['regexp_error']
                    tooltip = self.TOOLTIP['regexp_error'] + ': ' + error_msg
            self.search_text.lineEdit().setStyleSheet(stylesheet)
            self.search_text.setToolTip(tooltip)

            if self.is_code_editor and found:
                cursor = QTextCursor(self.editor.textCursor())
                TextHelper(self.editor).unfold_if_colapsed(cursor)

                if rehighlight or not self.editor.found_results:
                    self.highlight_timer.stop()
                    if start_highlight_timer:
                        self.highlight_timer.start()
                    else:
                        self.highlight_matches()
            else:
                self.clear_matches()

            number_matches = self.editor.get_number_matches(text,
                                                            case=case,
                                                            regexp=regexp,
                                                            word=word)
            if hasattr(self.editor, 'get_match_number'):
                match_number = self.editor.get_match_number(text,
                                                            case=case,
                                                            regexp=regexp,
                                                            word=word)
            else:
                match_number = 0
            self.change_number_matches(current_match=match_number,
                                       total_matches=number_matches)
            return found
Example #48
0
    def truncate_result(self, line, start, end):
        ellipsis = '...'
        max_line_length = 80
        max_num_char_fragment = 40

        html_escape_table = {
            "&": "&amp;",
            '"': "&quot;",
            "'": "&apos;",
            ">": "&gt;",
            "<": "&lt;",
        }

        def html_escape(text):
            """Produce entities within text."""
            return "".join(html_escape_table.get(c, c) for c in text)

        line = to_text_string(line)
        left, match, right = line[:start], line[start:end], line[end:]

        if len(line) > max_line_length:
            offset = (len(line) - len(match)) // 2

            left = left.split(' ')
            num_left_words = len(left)

            if num_left_words == 1:
                left = left[0]
                if len(left) > max_num_char_fragment:
                    left = ellipsis + left[-offset:]
                left = [left]

            right = right.split(' ')
            num_right_words = len(right)

            if num_right_words == 1:
                right = right[0]
                if len(right) > max_num_char_fragment:
                    right = right[:offset] + ellipsis
                right = [right]

            left = left[-4:]
            right = right[:4]

            if len(left) < num_left_words:
                left = [ellipsis] + left

            if len(right) < num_right_words:
                right = right + [ellipsis]

            left = ' '.join(left)
            right = ' '.join(right)

            if len(left) > max_num_char_fragment:
                left = ellipsis + left[-30:]

            if len(right) > max_num_char_fragment:
                right = right[:30] + ellipsis

        line_match_format = to_text_string('{0}<b>{1}</b>{2}')
        left = html_escape(left)
        right = html_escape(right)
        match = html_escape(match)
        trunc_line = line_match_format.format(left, match, right)
        return trunc_line
Example #49
0
def value_to_display(value, minmax=False, level=0):
    """Convert value for display purpose"""
    # To save current Numpy threshold
    np_threshold = FakeObject

    try:
        numeric_numpy_types = (int64, int32, int16, int8, uint64, uint32,
                               uint16, uint8, float64, float32, float16,
                               complex128, complex64, bool_)
        if ndarray is not FakeObject:
            # Save threshold
            np_threshold = get_printoptions().get('threshold')
            # Set max number of elements to show for Numpy arrays
            # in our display
            set_printoptions(threshold=10)
        if isinstance(value, recarray):
            if level == 0:
                fields = value.names
                display = 'Field names: ' + ', '.join(fields)
            else:
                display = 'Recarray'
        elif isinstance(value, MaskedArray):
            display = 'Masked array'
        elif isinstance(value, ndarray):
            if level == 0:
                if minmax:
                    try:
                        display = 'Min: %r\nMax: %r' % (value.min(),
                                                        value.max())
                    except (TypeError, ValueError):
                        if value.dtype.type in numeric_numpy_types:
                            display = str(value)
                        else:
                            display = default_display(value)
                elif value.dtype.type in numeric_numpy_types:
                    display = str(value)
                else:
                    display = default_display(value)
            else:
                display = 'Numpy array'
        elif any([type(value) == t for t in [list, set, tuple, dict]]):
            display = collections_display(value, level + 1)
        elif isinstance(value, Image):
            if level == 0:
                display = '%s  Mode: %s' % (address(value), value.mode)
            else:
                display = 'Image'
        elif isinstance(value, DataFrame):
            if level == 0:
                cols = value.columns
                if PY2 and len(cols) > 0:
                    # Get rid of possible BOM utf-8 data present at the
                    # beginning of a file, which gets attached to the first
                    # column header when headers are present in the first
                    # row.
                    # Fixes Issue 2514
                    try:
                        ini_col = to_text_string(cols[0], encoding='utf-8-sig')
                    except:
                        ini_col = to_text_string(cols[0])
                    cols = [ini_col] + [to_text_string(c) for c in cols[1:]]
                else:
                    cols = [to_text_string(c) for c in cols]
                display = 'Column names: ' + ', '.join(list(cols))
            else:
                display = 'Dataframe'
        elif isinstance(value, NavigableString):
            # Fixes Issue 2448
            display = to_text_string(value)
            if level > 0:
                display = u"'" + display + u"'"
        elif isinstance(value, Index):
            if level == 0:
                display = value.summary()
            else:
                display = 'Index'
        elif is_binary_string(value):
            # We don't apply this to classes that extend string types
            # See issue 5636
            if is_type_text_string(value):
                try:
                    display = to_text_string(value, 'utf8')
                    if level > 0:
                        display = u"'" + display + u"'"
                except:
                    display = value
                    if level > 0:
                        display = b"'" + display + b"'"
            else:
                display = default_display(value)
        elif is_text_string(value):
            # We don't apply this to classes that extend string types
            # See issue 5636
            if is_type_text_string(value):
                display = value
                if level > 0:
                    display = u"'" + display + u"'"
            else:
                display = default_display(value)
        elif (isinstance(value, datetime.date)
              or isinstance(value, datetime.timedelta)):
            display = str(value)
        elif (isinstance(value, NUMERIC_TYPES) or isinstance(value, bool)
              or isinstance(value, numeric_numpy_types)):
            display = repr(value)
        else:
            if level == 0:
                display = default_display(value)
            else:
                display = default_display(value, with_module=False)
    except:
        display = default_display(value)

    # Truncate display at 70 chars to avoid freezing Spyder
    # because of large displays
    if len(display) > 70:
        if is_binary_string(display):
            ellipses = b' ...'
        else:
            ellipses = u' ...'
        display = display[:70].rstrip() + ellipses

    # Restore Numpy threshold
    if np_threshold is not FakeObject:
        set_printoptions(threshold=np_threshold)

    return display
Example #50
0
    def populate_branch(self, editor, root_item, tree_cache=None):
        """
        Generates an outline of the editor's content and stores the result
        in a cache.
        """
        if tree_cache is None:
            tree_cache = {}

        for _l in list(tree_cache.keys()):
            # Checking if key is still in tree cache in case one of its
            # ancestors was deleted in the meantime (deleting all children):
            if _l not in tree_cache:
                continue

            # Removing deleted items
            if not tree_cache[_l][0].oedata.is_valid():
                remove_from_tree_cache(tree_cache, line=_l)
                continue

            # Moving cached items whose line changed
            block_line = tree_cache[_l][0].line
            if _l != block_line:
                if block_line in tree_cache:
                    remove_from_tree_cache(tree_cache, line=block_line)
                if _l in tree_cache:
                    tree_cache[block_line] = tree_cache[_l]
                    tree_cache.pop(_l)

        ancestors = [(root_item, 0)]
        cell_ancestors = [(root_item, 0)]
        previous_item = None
        previous_level = None
        prev_cell_level = None
        prev_cell_item = None

        for data in editor.outlineexplorer_data_list():
            try:
                line_nb = data.get_block_number()
                if line_nb is None:
                    continue
                line_nb += 1
            except AttributeError:
                continue
            level = None if data is None else data.fold_level
            citem, _d = tree_cache.get(line_nb, (None, ""))
            if citem is not None:
                # Check if underlying C++ object has been deleted
                try:
                    citem.text(0)
                except RuntimeError:
                    tree_cache.pop(line_nb)
                    citem, _d = (None, "")

            # Skip iteration if line is not the first line of a foldable block
            if level is None:
                if citem is not None:
                    remove_from_tree_cache(tree_cache, line=line_nb)
                continue

            # Searching for class/function statements
            not_class_nor_function = data.is_not_class_nor_function()
            if not not_class_nor_function:
                class_name = data.get_class_name()
                if class_name is None:
                    func_name = data.get_function_name()
                    if func_name is None:
                        if citem is not None:
                            remove_from_tree_cache(tree_cache, line=line_nb)
                        continue

            # Skip iteration for if/else/try/for/etc foldable blocks.
            if not_class_nor_function and not data.is_comment():
                if citem is not None:
                    remove_from_tree_cache(tree_cache, line=line_nb)
                continue

            if citem is not None:
                cname = to_text_string(citem.text(0))
                cparent = citem.parent()
                clevel = citem.level()

            # Blocks for Cell Groups.
            if (data is not None and data.def_type == data.CELL
                    and self.group_cells):
                preceding = (root_item
                             if previous_item is None else previous_item)
                cell_level = data.cell_level
                if prev_cell_level is not None:
                    if cell_level == prev_cell_level:
                        pass
                    elif cell_level > prev_cell_level:
                        cell_ancestors.append(
                            (prev_cell_item, prev_cell_level))
                    else:
                        while (len(cell_ancestors) > 1
                               and cell_level <= prev_cell_level):
                            cell_ancestors.pop(-1)
                            _item, prev_cell_level = cell_ancestors[-1]
                parent, _level = cell_ancestors[-1]
                if citem is not None:
                    if data.text == cname and level == clevel:
                        previous_level = clevel
                        previous_item = citem
                        continue
                    else:
                        remove_from_tree_cache(tree_cache, line=line_nb)
                item = CellItem(data, parent, preceding)
                debug = "%s -- %s/%s" % (str(
                    item.line).rjust(6), to_text_string(
                        item.parent().text(0)), to_text_string(item.text(0)))
                tree_cache[line_nb] = (item, debug)
                ancestors = [(item, 0)]
                prev_cell_level = cell_level
                prev_cell_item = item
                previous_item = item
                continue

            # Blocks for Code Groups.
            if previous_level is not None:
                if level == previous_level:
                    pass
                elif level > previous_level:
                    ancestors.append((previous_item, previous_level))
                else:
                    while len(ancestors) > 1 and level <= previous_level:
                        ancestors.pop(-1)
                        _item, previous_level = ancestors[-1]
            parent, _level = ancestors[-1]

            preceding = root_item if previous_item is None else previous_item
            if not_class_nor_function and data.is_comment():
                if not self.show_comments:
                    if citem is not None:
                        remove_from_tree_cache(tree_cache, line=line_nb)
                    continue
                if citem is not None:
                    if data.text == cname and level == clevel:
                        previous_level = clevel
                        previous_item = citem
                        continue
                    else:
                        remove_from_tree_cache(tree_cache, line=line_nb)
                if data.def_type == data.CELL:
                    item = CellItem(data, parent, preceding)
                else:
                    item = CommentItem(data, parent, preceding)
            elif class_name is not None:
                if citem is not None:
                    if (class_name == cname and level == clevel
                            and parent is cparent):
                        previous_level = clevel
                        previous_item = citem
                        continue
                    else:
                        remove_from_tree_cache(tree_cache, line=line_nb)
                item = ClassItem(data, parent, preceding)
            else:
                if citem is not None:
                    if (func_name == cname and level == clevel
                            and parent is cparent):
                        previous_level = clevel
                        previous_item = citem
                        continue
                    else:
                        remove_from_tree_cache(tree_cache, line=line_nb)
                item = FunctionItem(data, parent, preceding)

            debug = "%s -- %s/%s" % (str(
                item.line).rjust(6), to_text_string(
                    item.parent().text(0)), to_text_string(item.text(0)))
            tree_cache[line_nb] = (item, debug)
            previous_level = level
            previous_item = item

        return tree_cache
Example #51
0
 def is_valid(self, qstr=None):
     """Return True if string is valid"""
     if qstr is None:
         qstr = self.currentText()
     return is_module_or_package(to_text_string(qstr))
Example #52
0
def handle_qbytearray(obj, encoding):
    """Qt/Python2/3 compatibility helper."""
    if isinstance(obj, QByteArray):
        obj = obj.data()

    return to_text_string(obj, encoding=encoding)
Example #53
0
 def set_current_text(self, text):
     """Sets the text of the QLineEdit of the QComboBox."""
     self.lineEdit().setText(to_text_string(text))
Example #54
0
 def url_combo_activated(self, valid):
     """Load URL from combo box first item"""
     text = to_text_string(self.url_combo.currentText())
     self.go_to(self.text_to_url(text))
Example #55
0
    def get_options(self, all=False):
        # Getting options
        self.search_text.lineEdit().setStyleSheet("")
        self.exclude_pattern.lineEdit().setStyleSheet("")

        utext = to_text_string(self.search_text.currentText())
        if not utext:
            return
        try:
            texts = [(utext.encode('utf-8'), 'utf-8')]
        except UnicodeEncodeError:
            texts = []
            for enc in self.supported_encodings:
                try:
                    texts.append((utext.encode(enc), enc))
                except UnicodeDecodeError:
                    pass
        text_re = self.edit_regexp.isChecked()
        exclude = to_text_string(self.exclude_pattern.currentText())
        exclude_re = self.exclude_regexp.isChecked()
        case_sensitive = self.case_button.isChecked()
        python_path = False

        if not case_sensitive:
            texts = [(text[0].lower(), text[1]) for text in texts]

        file_search = self.path_selection_combo.is_file_search()
        path = self.path_selection_combo.get_current_searchpath()

        if not exclude_re:
            items = [fnmatch.translate(item.strip())
                     for item in exclude.split(",")
                     if item.strip() != '']
            exclude = '|'.join(items)

        # Validate regular expressions:

        try:
            if exclude:
                exclude = re.compile(exclude)
        except Exception:
            exclude_edit = self.exclude_pattern.lineEdit()
            exclude_edit.setStyleSheet(self.REGEX_INVALID)
            return None

        if text_re:
            try:
                texts = [(re.compile(x[0]), x[1]) for x in texts]
            except Exception:
                self.search_text.lineEdit().setStyleSheet(self.REGEX_INVALID)
                return None

        if all:
            search_text = [to_text_string(self.search_text.itemText(index))
                           for index in range(self.search_text.count())]
            exclude = [to_text_string(self.exclude_pattern.itemText(index))
                       for index in range(self.exclude_pattern.count())]
            path_history = self.path_selection_combo.get_external_paths()
            exclude_idx = self.exclude_pattern.currentIndex()
            more_options = self.more_options.isChecked()
            return (search_text, text_re, [],
                    exclude, exclude_idx, exclude_re,
                    python_path, more_options, case_sensitive, path_history)
        else:
            return (path, file_search, exclude, texts, text_re, case_sensitive)
Example #56
0
 def createWindow(self, webwindowtype):
     import webbrowser
     webbrowser.open(to_text_string(self.url().toString()))
Example #57
0
    def start(self, wdir=None, args=None, pythonpath=None):
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)
        if args is None:
            args = self._last_args
            if args is None:
                args = []
        if pythonpath is None:
            pythonpath = self._last_pythonpath
        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Profiling, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self.finished(ec, es))
        self.stop_button.clicked.connect(self.kill)

        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in self.process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            self.process.setProcessEnvironment(processEnvironment)

        self.output = ''
        self.error_output = ''
        self.stopped = False

        p_args = ['-m', 'cProfile', '-o', self.DATAPATH]
        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            p_args.append(osp.normpath(filename).replace(os.sep, '/'))
        else:
            p_args.append(filename)
        if args:
            p_args.extend(shell_split(args))
        executable = sys.executable
        if executable.endswith("spyder.exe"):
            # py2exe distribution
            executable = "python.exe"
        self.process.start(executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
Example #58
0
    def setup_and_check(self, data, title='', readonly=False,
                        xlabels=None, ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        readonly = readonly or not self.data.flags.writeable
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)

        if data.ndim > 3:
            self.error(_("Arrays with more than 3 dimensions are not "
                         "supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(_("The 'xlabels' argument length do no match array "
                         "column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(_("The 'ylabels' argument length do no match array row "
                         "number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn == 'object':
                # If the array doesn't have shape, we can't display it
                if data.shape == ():
                    self.error(_("Object arrays without shape are not "
                                 "supported"))
                    return False
                # We don't know what's inside these arrays, so we can't handle
                # edits
                self.readonly = readonly = True
            elif (dtn not in SUPPORTED_FORMATS and not dtn.startswith('str')
                    and not dtn.startswith('unicode')):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False

        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - " + _("NumPy array")
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)

        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(ArrayEditorWidget(self, data[name],
                                                       readonly, xlabels,
                                                       ylabels))
        elif is_masked_array:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly,
                                                   xlabels, ylabels))
        elif data.ndim == 3:
            pass
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        if self.arraywidget:
            self.arraywidget.model.dataChanged.connect(
                self.save_and_close_enable)
        self.stack.currentChanged.connect(self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array or data.ndim == 3:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not is_text_string(title):
                            title = repr(title)
                        text += ' - '+title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            if data.ndim == 3:
                # QSpinBox
                self.index_spin = QSpinBox(self, keyboardTracking=False)
                self.index_spin.valueChanged.connect(self.change_active_widget)
                # QComboBox
                names = [str(i) for i in range(3)]
                ra_combo = QComboBox(self)
                ra_combo.addItems(names)
                ra_combo.currentIndexChanged.connect(self.current_dim_changed)
                # Adding the widgets to layout
                label = QLabel(_("Axis:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(ra_combo)
                self.shape_label = QLabel()
                btn_layout.addWidget(self.shape_label)
                label = QLabel(_("Index:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(self.index_spin)
                self.slicing_label = QLabel()
                btn_layout.addWidget(self.slicing_label)
                # set the widget to display when launched
                self.current_dim_changed(self.last_dim)
            else:
                ra_combo = QComboBox(self)
                ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
                ra_combo.addItems(names)
                btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(_("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)

        btn_layout.addStretch()

        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        return True
Example #59
0
 def translate_dumb(x):
     if not is_unicode(x):
         return to_text_string(x, "utf-8")
     return x
Example #60
0
def get_search_score(query, choice, ignore_case=True, apply_regex=True,
                     template='{}'):
    """Returns a tuple with the enriched text (if a template is provided) and
    a score for the match.

    Parameters
    ----------
    query : str
        String with letters to search in choice (in order of appearance).
    choice : str
        Sentence/words in which to search for the 'query' letters.
    ignore_case : bool, optional
        Optional value perform a case insensitive search (True by default).
    apply_regex : bool, optional
        Optional value (True by default) to perform a regex search. Useful
        when this function is called directly.
    template : str, optional
        Optional template string to surround letters found in choices. This is
        useful when using a rich text editor ('{}' by default).
        Examples: '<b>{}</b>', '<code>{}</code>', '<i>{}</i>'

    Returns
    -------
    results : tuple
        Tuples where the first item is the text (enriched if a template was
        used) and the second item is a search score.

    Notes
    -----
    The score is given according the following precedence (high to low):

    - Letters in one word and no spaces with exact match.
      Example: 'up' in 'up stroke'
    - Letters in one word and no spaces with partial match.
      Example: 'up' in 'upstream stroke'
    - Letters in one word but with skip letters.
      Example: 'cls' in 'close up'
    - Letters in two or more words
      Example: 'cls' in 'car lost'
    """
    original_choice = to_text_string(choice, encoding='utf-8')
    result = (original_choice, NOT_FOUND_SCORE)

    # Handle empty string case
    if not query:
        return result

    query = to_text_string(query, encoding='utf-8')
    choice = to_text_string(choice, encoding='utf-8')

    if ignore_case:
        query = query.lower()
        choice = choice.lower()

    if apply_regex:
        pattern = get_search_regex(query, ignore_case=ignore_case)
        r = re.search(pattern, choice)
        if r is None:
            return result
    else:
        sep = u'-'  # Matches will be replaced by this character
        let = u'x'  # Nonmatches (except spaed) will be replaced by this
        score = 0

        exact_words = [query == to_text_string(word, encoding='utf-8')
                       for word in choice.split(u' ')]
        partial_words = [query in word for word in choice.split(u' ')]

        if any(exact_words) or any(partial_words):
            pos_start = choice.find(query)
            pos_end = pos_start + len(query)
            score += pos_start
            text = choice.replace(query, sep*len(query), 1)

            enriched_text = original_choice[:pos_start] +\
                template.format(original_choice[pos_start:pos_end]) +\
                original_choice[pos_end:]

        if any(exact_words):
            # Check if the query words exists in a word with exact match
            score += 1
        elif any(partial_words):
            # Check if the query words exists in a word with partial match
            score += 100
        else:
            # Check letter by letter
            text = [l for l in original_choice]
            if ignore_case:
                temp_text = [l.lower() for l in original_choice]
            else:
                temp_text = text[:]

            # Give points to start of string
            score += temp_text.index(query[0])

            # Find the query letters and replace them by `sep`, also apply
            # template as needed for enricching the letters in the text
            enriched_text = text[:]
            for char in query:
                if char != u'' and char in temp_text:
                    index = temp_text.index(char)
                    enriched_text[index] = template.format(text[index])
                    text[index] = sep
                    temp_text = [u' ']*(index + 1) + temp_text[index+1:]

        enriched_text = u''.join(enriched_text)

        patterns_text = []
        for i, char in enumerate(text):
            if char != u' ' and char != sep:
                new_char = let
            else:
                new_char = char
            patterns_text.append(new_char)
        patterns_text = u''.join(patterns_text)
        for i in reversed(range(1, len(query) + 1)):
            score += (len(query) - patterns_text.count(sep*i))*100000

        temp = patterns_text.split(sep)
        while u'' in temp:
            temp.remove(u'')
        if not patterns_text.startswith(sep):
            temp = temp[1:]
        if not patterns_text.endswith(sep):
            temp = temp[:-1]

        for pat in temp:
            score += pat.count(u' ')*10000
            score += pat.count(let)*100

    return original_choice, enriched_text, score