Skip to content

Commit 0f323c9

Browse files
committed
Tidy up the parser to return a Term | None, instead of tuple[Term, ...] (which was sometimes empty
1 parent 240fbea commit 0f323c9

File tree

3 files changed

+56
-75
lines changed

3 files changed

+56
-75
lines changed

simple_repository_browser/_search.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,25 +122,20 @@ class SearchCompiler:
122122
"""
123123

124124
@classmethod
125-
def compile(cls, terms: Term | tuple[Term, ...]) -> SQLBuilder:
125+
def compile(cls, term: Term | None) -> SQLBuilder:
126126
"""Compile search terms into SQL WHERE and ORDER BY clauses."""
127-
# Normalise to tuple of terms
128-
if not isinstance(terms, tuple):
129-
terms = (terms,) if terms else ()
130-
131-
if len(terms) == 0:
127+
if term is None:
132128
return SQLBuilder(
133129
where_clause="",
134130
where_params=(),
135131
order_clause="",
136132
order_params=(),
137133
search_context=SearchContext(),
138134
)
139-
assert len(terms) == 1
140135

141136
# Build WHERE clause and collect context
142137
context = SearchContext()
143-
where_clause, where_params, final_context = cls._visit_term(terms[0], context)
138+
where_clause, where_params, final_context = cls._visit_term(term, context)
144139

145140
# Build ORDER BY clause based on collected context
146141
order_clause, order_params = cls._build_ordering_from_context(final_context)
@@ -338,14 +333,14 @@ def _build_ordering_from_context(
338333
return order_clause, tuple(all_params)
339334

340335

341-
def build_sql(terms: Term | tuple[Term, ...]) -> SQLBuilder:
336+
def build_sql(term: Term | None) -> SQLBuilder:
342337
"""Build SQL WHERE and ORDER BY clauses from search terms."""
343-
return SearchCompiler.compile(terms)
338+
return SearchCompiler.compile(term)
344339

345340

346341
def query_to_sql(query) -> SQLBuilder:
347-
terms = parse(query)
348-
return build_sql(terms)
342+
term = parse(query)
343+
return build_sql(term)
349344

350345

351346
grammar = parsley.makeGrammar(
@@ -376,8 +371,8 @@ def query_to_sql(query) -> SQLBuilder:
376371
|filter:filter -> filter
377372
|'-' filters:filters -> Not(filters)
378373
)
379-
search_terms = (filters+:filters -> tuple(filters)
380-
| -> ())
374+
search_terms = (filters:filters -> filters
375+
| -> None)
381376
"""),
382377
{
383378
"And": And,
@@ -389,7 +384,7 @@ def query_to_sql(query) -> SQLBuilder:
389384
)
390385

391386

392-
def parse(query: str) -> typing.Tuple[Term, ...]:
387+
def parse(query: str) -> Term | None:
393388
return grammar(query.strip()).search_terms()
394389

395390

simple_repository_browser/model.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,14 @@ async def project_query(
114114
self, query: str, page_size: int, page: int
115115
) -> QueryResultModel:
116116
try:
117-
search_terms = _search.parse(query)
117+
search_term = _search.parse(query)
118118
except _search.ParseError:
119119
raise errors.InvalidSearchQuery("Invalid search pattern")
120120

121-
if not search_terms:
121+
if search_term is None:
122122
raise errors.InvalidSearchQuery("Please specify a search query")
123123
try:
124-
sql_builder = _search.build_sql(search_terms)
124+
sql_builder = _search.build_sql(search_term)
125125
except ValueError as err:
126126
raise errors.InvalidSearchQuery(f"Search query invalid ({str(err)})")
127127

simple_repository_browser/tests/test_search.py

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,94 +13,80 @@
1313
@pytest.mark.parametrize(
1414
["query", "expected_expression_graph"],
1515
[
16-
("", ()),
17-
pytest.param("some-name", (Filter(FilterOn.name_or_summary, "some-name"),)),
16+
("", None),
17+
pytest.param("some-name", Filter(FilterOn.name_or_summary, "some-name")),
1818
pytest.param(
1919
"some name",
20-
(
21-
_search.And(
22-
Filter(FilterOn.name_or_summary, "some"),
23-
Filter(FilterOn.name_or_summary, "name"),
24-
),
20+
_search.And(
21+
Filter(FilterOn.name_or_summary, "some"),
22+
Filter(FilterOn.name_or_summary, "name"),
2523
),
2624
),
27-
pytest.param("som*name", (Filter(FilterOn.name_or_summary, "som*name"),)),
28-
pytest.param('"some name"', (Filter(FilterOn.name_or_summary, '"some name"'),)),
29-
pytest.param('"some-name"', (Filter(FilterOn.name_or_summary, '"some-name"'),)),
30-
pytest.param('"CASE"', (Filter(FilterOn.name_or_summary, '"CASE"'),)),
31-
pytest.param("-foo", (_search.Not(Filter(FilterOn.name_or_summary, "foo")),)),
25+
pytest.param("som*name", Filter(FilterOn.name_or_summary, "som*name")),
26+
pytest.param('"some name"', Filter(FilterOn.name_or_summary, '"some name"')),
27+
pytest.param('"some-name"', Filter(FilterOn.name_or_summary, '"some-name"')),
28+
pytest.param('"CASE"', Filter(FilterOn.name_or_summary, '"CASE"')),
29+
pytest.param("-foo", _search.Not(Filter(FilterOn.name_or_summary, "foo"))),
3230
pytest.param(
33-
'-"foo bar"', (_search.Not(Filter(FilterOn.name_or_summary, '"foo bar"')),)
31+
'-"foo bar"', _search.Not(Filter(FilterOn.name_or_summary, '"foo bar"'))
3432
),
3533
pytest.param(
36-
'-name:"foo bar"', (_search.Not(Filter(FilterOn.name, '"foo bar"')),)
34+
'-name:"foo bar"', _search.Not(Filter(FilterOn.name, '"foo bar"'))
3735
),
38-
pytest.param("name:foo", (Filter(FilterOn.name, "foo"),)),
36+
pytest.param("name:foo", Filter(FilterOn.name, "foo")),
3937
pytest.param(
4038
"name:foo OR name:bar",
41-
(
42-
_search.Or(
43-
Filter(FilterOn.name, "foo"),
44-
Filter(FilterOn.name, "bar"),
45-
),
39+
_search.Or(
40+
Filter(FilterOn.name, "foo"),
41+
Filter(FilterOn.name, "bar"),
4642
),
4743
),
4844
pytest.param(
4945
'name:foo AND "fiddle AND sticks"',
50-
(
51-
_search.And(
52-
Filter(FilterOn.name, "foo"),
53-
Filter(FilterOn.name_or_summary, '"fiddle AND sticks"'),
54-
),
46+
_search.And(
47+
Filter(FilterOn.name, "foo"),
48+
Filter(FilterOn.name_or_summary, '"fiddle AND sticks"'),
5549
),
5650
),
57-
pytest.param("summary:foo", (Filter(FilterOn.summary, "foo"),)),
51+
pytest.param("summary:foo", Filter(FilterOn.summary, "foo")),
5852
pytest.param(
5953
'name:"NAME OR" AND "fiddle AND sticks"',
60-
(
61-
_search.And(
62-
Filter(FilterOn.name, '"NAME OR"'),
63-
Filter(FilterOn.name_or_summary, '"fiddle AND sticks"'),
64-
),
54+
_search.And(
55+
Filter(FilterOn.name, '"NAME OR"'),
56+
Filter(FilterOn.name_or_summary, '"fiddle AND sticks"'),
6557
),
6658
),
67-
pytest.param("(((a)))", (Filter(FilterOn.name_or_summary, "a"),)),
59+
pytest.param("(((a)))", Filter(FilterOn.name_or_summary, "a")),
6860
pytest.param(
6961
"(((a) OR (b)))",
70-
(
71-
_search.Or(
72-
Filter(FilterOn.name_or_summary, "a"),
73-
Filter(FilterOn.name_or_summary, "b"),
74-
),
62+
_search.Or(
63+
Filter(FilterOn.name_or_summary, "a"),
64+
Filter(FilterOn.name_or_summary, "b"),
7565
),
7666
),
7767
pytest.param(
7868
"(a AND b) OR (c AND d)",
79-
(
80-
_search.Or(
81-
_search.And(
82-
Filter(FilterOn.name_or_summary, "a"),
83-
Filter(FilterOn.name_or_summary, "b"),
84-
),
85-
_search.And(
86-
Filter(FilterOn.name_or_summary, "c"),
87-
Filter(FilterOn.name_or_summary, "d"),
88-
),
69+
_search.Or(
70+
_search.And(
71+
Filter(FilterOn.name_or_summary, "a"),
72+
Filter(FilterOn.name_or_summary, "b"),
73+
),
74+
_search.And(
75+
Filter(FilterOn.name_or_summary, "c"),
76+
Filter(FilterOn.name_or_summary, "d"),
8977
),
9078
),
9179
),
9280
pytest.param(
9381
"((a AND b)) OR (c AND -d)",
94-
(
95-
_search.Or(
96-
_search.And(
97-
Filter(FilterOn.name_or_summary, "a"),
98-
Filter(FilterOn.name_or_summary, "b"),
99-
),
100-
_search.And(
101-
Filter(FilterOn.name_or_summary, "c"),
102-
_search.Not(Filter(FilterOn.name_or_summary, "d")),
103-
),
82+
_search.Or(
83+
_search.And(
84+
Filter(FilterOn.name_or_summary, "a"),
85+
Filter(FilterOn.name_or_summary, "b"),
86+
),
87+
_search.And(
88+
Filter(FilterOn.name_or_summary, "c"),
89+
_search.Not(Filter(FilterOn.name_or_summary, "d")),
10490
),
10591
),
10692
),

0 commit comments

Comments
 (0)