Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/operations/filtering/parser.py: 100.00%
21 statements
« prev ^ index » next coverage.py v7.4.1, created at 2025-04-14 16:27 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2025-04-14 16:27 +0000
1# Copyright (C) 2020 Bloomberg LP
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# <http://www.apache.org/licenses/LICENSE-2.0>
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
16from lark import Lark
17from lark.exceptions import LarkError
19from buildgrid.server.exceptions import InvalidArgumentError
20from buildgrid.server.operations.filtering import OperationFilter
21from buildgrid.server.operations.filtering.interpreter import FilterTreeInterpreter
24class FilterParser:
25 """
26 Utility class primarily used to parse a filter string and return a list a OperationFilters.
27 """
29 lark_parser = Lark.open_from_package(
30 "buildgrid.server.operations.filtering", "filter_grammar.lark", start="filter_phrase"
31 )
33 @staticmethod
34 def parse_listoperations_filters(filter_string: str) -> list[OperationFilter]:
35 """Separate the lowercase filter string into individual components, and return a map containing
36 the relevant filters to use.
38 Filter strings take the following form: key1=value1&key2=value2,value3&key3=value4
39 All spaces in the input are ignored.
41 Filter options are separated with ampersands (&).
42 If a key is repeated, the combined set of values are treated as a logical conjunction (AND).
44 """
45 operation_filters: list[OperationFilter] = []
47 # Handle empty input separately to simplify grammar
48 if not filter_string.strip():
49 return operation_filters
51 try:
52 filter_phrase_tree = FilterParser.lark_parser.parse(filter_string)
53 except LarkError as e:
54 raise InvalidArgumentError(
55 f"Error parsing filter string. See docs for correct filter string syntax. "
56 f"Parser error follows: [{str(e)}]"
57 )
59 try:
60 operation_filters = FilterTreeInterpreter().visit(filter_phrase_tree)
61 except InvalidArgumentError as e:
62 raise InvalidArgumentError(
63 f"Invalid filter string [{filter_string}]. See docs for correct filter string syntax and values."
64 ) from e
66 return operation_filters