Pull to refresh

Отладчик регулярок pyRoge

Reading time7 min
Views749
Привет, %username%, если ты тоже страдаешь от постоянных проблем с набиранием регулярных выражений в питоне — тебе сюда!
Часто в работе сталкиваюсь с написанием регулярных выражений, и постоянно испытывал некий дискомфорт с их отладкой. В мире существует уйма разного рода отладчиков, но проблема в той что и у регулярок, существуют разные диалекты, например взяв регулярное выражения из Ruby, вы не сможете запустить его в Python, хотя казалось бы братья). Поэтому, я написал небольшую программку на Python, позволившую мне быстро решать задачи по профилю. Логику работы программки подсмотрел где-то в интернетах(был отладчик для руби). Да, также я решил прокомментировать код, потому что его немного, но для самых нетерпеливых ссылки на исходники здесь.
В качестве начального примера загружается решение любимой задачки timurvпродолжите последовательность Look-And-Say. Выглядит «примерно» так:


Тестировал на Python2.5, PyQt4.4, на машинах Windows Xp, Ubuntu 9.10.

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    ui = Ui_mwd()
    form = Wnd(ui)
    ui.setupUi(form)
    form.show()
    form.callSearchRegExp()             # for first time evalution
    sys.exit(app.exec_())

ui_Wnd — это класс, модифицирующий экземпляр класса QtGui.QMainWindow — генерируется автоматически pyuic'ом. Wnd — мой потомок QMainWindow, о нем ниже. Немного неудобно строен pyQt — если посмотреть, то все контролы висящие на окне принадлежат классу ui_Wnd, но если мы в том же QtDesignerе определим события, то они лягут уже на потомка QMainwindow, поэтому в коде и такая сумятица, и необходимость передачи Wnd(ui) — что мы в своем классе Wnd могли обращаться к контролам.

class Wnd(QtGui.QMainWindow)
    _timer = None
    SIGNAL_prepared = QtCore.SIGNAL('prepared(QString)')
    SIGNAL_excRaise = QtCore.SIGNAL('exceptionRaise(QString)')
    _observer = ObserveHtmlWidget()
 
    def __init__(self, ui)
        QtGui.QMainWindow.__init__(self)
        self.ui = ui
        QtCore.QObject.connect(selfself.SIGNAL_preparedself.onPrepared)
        QtCore.QObject.connect(selfself.SIGNAL_excRaiseself.onRaiseException)
 
    def callSearchRegExp(self):
        txt, rg = unicode((self.ui.eText.toPlainText()))unicode(self.ui.eReg.toPlainText())
        self.ui.statusBar.clearMessage()
        try:
            data = self._observer(txt, rg)
        except SmartError, e:
            self.emit(self.SIGNAL_excRaise, e[0]);
            return
 
        if (data):
            self.emit(self.SIGNAL_prepared, data[0])
            self.showGroups(data[1])
 
    def callTimerForRegExp(self):
        if (self._timer):
            self._timer.cancel()
        self._timer = threading.Timer(1self.callSearchRegExp)
        self._timer.start()    
 
    def onPrepared(self, value):
        self.ui.eHtml.setHtml(value)
 
    def onRaiseException(self, value):
        self.ui.statusBar.showMessage(value)
 
    def showGroups(self, rows):
        self.ui.lvas.clear()
        for e in rows:
            if isinstance(e, tuple):
                prepared = [inverseReplace(i) for i in e]
                self.ui.lvas.addItem(', '.join(prepared))
            else:
                self.ui.lvas.addItem(inverseReplace(e))

Это класс главного окна, в принципе здесь все прозрачно:
_timer — это устроиство которое позволит на следить за окочанием ввода
пара сигналов, для оповещения главного потока программы о том, что регэксп был вычислен или не вычислен в фоне
observer — собствественно счетная машинка

callTimerForRegExp — ждет чтобы прошла секунда, после ввода последнего символа и вызывает callSearchRegExp, он уже вызывает счетную машинку, которая возвращает окну либо данные по регэкспу, либо задекларированный Exception — о корректной ошибке, внутри программы. showGroups — эта штука позволяет верно отрисовавыть группы которые будут найдены регуляркой.

# advanced regexp evaluting
class ObserveHtmlWidget(object)
    phtml = '<span style="background-color:#BFE4FF; color:#0066B3; padding:2px;">%s</span>'
    def _exec(self, func, value, ignoreExc):
        try:
            value = func(value)
        except ignoreExc, e:
            raise SmartError('%s: %s' % (type(e).__name__, e[0]))
        return value
 
    def __call__(self, text='', regexp=''):
        if regexp=='':
            return
        html, groups = ''[]
 
        text,regexp = htmlReplace(text), protectedReplace(regexp)
        ptn = self._exec(re.compile, regexp, re.error)
        matches = self._exec(ptn.search, text, Exception)
        while (matches and text<>''):
            s, e, v = matches.start(), matches.end(), matches.group()
            groups.append(matches.groups())
            html += text[:s]+self.phtml % v
            if not e0
                text = text[1:]
            else:               # null valued iterations
                text = text[e:]
            matches = ptn.search(text)
        return (html+text, groups)


Это собственно калькулятор, он получает текст и регулярку к нему, дальше бежит по совпадениям, и в случае если все в порядке возвращает текст с подсвеченными совпадениями, и список групп. В случае ошибки, возвращается корректный инстанс ошибки SmartError.

# make simple replace
class HtmlReplacer(object):
    mods = {'>''&gt;''<''&lt;'}
    def __call__(self, raw):
        for k,v in self.mods.iteritems():
            raw = raw.replace(k,v)
        return raw
 
# make replace in regexp
class ProtectedHtmlReplacer(HtmlReplacer):
    mods = {'\>''&gt;''\<''&lt;'}
 
# inverse replace
class InverseHtmlReplacer(HtmlReplacer):
    mods = {'&gt;''>''&lt;''<'}
 
# create instances of objects, for make easier call
htmlReplace, protectedReplace, inverseReplace = HtmlReplacer(), ProtectedHtmlReplacer(), InverseHtmlReplacer()
 
# this project valid exception
class SmartError(Exception)pass


Ну а это бесплатное приложение к коду, замены html тегов, потому как на окне контрол, который подсвечивает текст, использует html-mode, и для того чтобы корректно отбразить символы < > — приходиться их менять на соответствующие коды html.

Проект доступен полностью здесь.
Tags:
Hubs:
Total votes 34: ↑29 and ↓5+24
Comments16

Articles