From ad5a6f68c127e110b06945ab7706654286358a9a Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Fri, 10 Apr 2015 11:32:48 -0400 Subject: [PATCH] Support regexes in search Implement support for regular expressions in search terms. Change-Id: I090d23f50b0ce8e61b6257bea9f84a49aaf57553 --- gertty/app.py | 5 +++++ gertty/db.py | 10 ++++++++++ gertty/search/__init__.py | 2 +- gertty/search/parser.py | 26 +++++++++++++++++--------- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/gertty/app.py b/gertty/app.py index d021ba1..a69fab1 100644 --- a/gertty/app.py +++ b/gertty/app.py @@ -27,6 +27,7 @@ import urlparse import warnings import webbrowser +import sqlalchemy.exc import urwid from gertty import db @@ -417,6 +418,10 @@ class App(object): changes = session.getChanges(query) except gertty.search.SearchSyntaxError as e: return self.error(e.message) + except sqlalchemy.exc.OperationalError as e: + return self.error(e.message) + except Exception as e: + return self.error(str(e)) change_key = None if len(changes) == 1: change_key = changes[0].key diff --git a/gertty/db.py b/gertty/db.py index 5396644..03d675a 100644 --- a/gertty/db.py +++ b/gertty/db.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import re import time import logging import threading @@ -497,6 +498,15 @@ mapper(Approval, approval_table, properties=dict( mapper(PendingCherryPick, pending_cherry_pick_table) mapper(SyncQuery, sync_query_table) +def match(expr, item): + if item is None: + return False + return re.match(expr, item) is not None + +@sqlalchemy.event.listens_for(sqlalchemy.engine.Engine, "connect") +def add_sqlite_match(dbapi_connection, connection_record): + dbapi_connection.create_function("matches", 2, match) + class Database(object): def __init__(self, app): self.log = logging.getLogger('gertty.db') diff --git a/gertty/search/__init__.py b/gertty/search/__init__.py index 2dbfc1a..6eef69c 100644 --- a/gertty/search/__init__.py +++ b/gertty/search/__init__.py @@ -62,7 +62,7 @@ class SearchCompiler(object): if __name__ == '__main__': class Dummy(object): pass - query = 'status:open AND topic:enable_swift' + query = 'ref:^refs/heads/foo.*' lexer = tokenizer.SearchTokenizer() lexer.input(query) while True: diff --git a/gertty/search/parser.py b/gertty/search/parser.py index 203620e..69ae522 100644 --- a/gertty/search/parser.py +++ b/gertty/search/parser.py @@ -16,7 +16,7 @@ import datetime import re import ply.yacc as yacc -from sqlalchemy.sql.expression import and_, or_, not_, select +from sqlalchemy.sql.expression import and_, or_, not_, select, func import gertty.db import gertty.search @@ -157,8 +157,10 @@ def SearchParser(): def p_project_term(p): '''project_term : OP_PROJECT string''' - #TODO: support regex - p[0] = gertty.db.project_table.c.name == p[2] + if p[2].startswith('^'): + p[0] = func.matches(p[2], gertty.db.project_table.c.name) + else: + p[0] = gertty.db.project_table.c.name == p[2] def p_project_key_term(p): '''project_key_term : OP_PROJECT_KEY NUMBER''' @@ -166,18 +168,24 @@ def SearchParser(): def p_branch_term(p): '''branch_term : OP_BRANCH string''' - #TODO: support regex - p[0] = gertty.db.change_table.c.branch == p[2] + if p[2].startswith('^'): + p[0] = func.matches(p[2], gertty.db.change_table.c.branch) + else: + p[0] = gertty.db.change_table.c.branch == p[2] def p_topic_term(p): '''topic_term : OP_TOPIC string''' - #TODO: support regex - p[0] = gertty.db.change_table.c.topic == p[2] + if p[2].startswith('^'): + p[0] = func.matches(p[2], gertty.db.change_table.c.topic) + else: + p[0] = gertty.db.change_table.c.topic == p[2] def p_ref_term(p): '''ref_term : OP_REF string''' - #TODO: support regex - p[0] = gertty.db.change_table.c.branch == p[2][len('refs/heads/'):] + if p[2].startswith('^'): + p[0] = func.matches(p[2], 'refs/heads/'+gertty.db.change_table.c.branch) + else: + p[0] = gertty.db.change_table.c.branch == p[2][len('refs/heads/'):] label_re = re.compile(r'(?P