Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/operations/filtering/parser.py: 100.00%

22 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-10-04 17:48 +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. 

14 

15 

16from typing import List 

17 

18from lark import Lark 

19from lark.exceptions import LarkError 

20 

21from buildgrid.server.exceptions import InvalidArgumentError 

22from buildgrid.server.operations.filtering import OperationFilter 

23from buildgrid.server.operations.filtering.interpreter import FilterTreeInterpreter 

24 

25 

26class FilterParser: 

27 """ 

28 Utility class primarily used to parse a filter string and return a list a OperationFilters. 

29 """ 

30 

31 lark_parser = Lark.open_from_package( 

32 "buildgrid.server.operations.filtering", "filter_grammar.lark", start="filter_phrase" 

33 ) 

34 

35 @staticmethod 

36 def parse_listoperations_filters(filter_string: str) -> List[OperationFilter]: 

37 """Separate the lowercase filter string into individual components, and return a map containing 

38 the relevant filters to use. 

39 

40 Filter strings take the following form: key1=value1&key2=value2,value3&key3=value4 

41 All spaces in the input are ignored. 

42 

43 Filter options are separated with ampersands (&). 

44 If a key is repeated, the combined set of values are treated as a logical conjunction (AND). 

45 

46 """ 

47 operation_filters: List[OperationFilter] = [] 

48 

49 # Handle empty input separately to simplify grammar 

50 if not filter_string.strip(): 

51 return operation_filters 

52 

53 try: 

54 filter_phrase_tree = FilterParser.lark_parser.parse(filter_string) 

55 except LarkError as e: 

56 raise InvalidArgumentError( 

57 f"Error parsing filter string. See docs for correct filter string syntax. " 

58 f"Parser error follows: [{str(e)}]" 

59 ) 

60 

61 try: 

62 operation_filters = FilterTreeInterpreter().visit(filter_phrase_tree) 

63 except InvalidArgumentError as e: 

64 raise InvalidArgumentError( 

65 f"Invalid filter string [{filter_string}]. See docs for correct filter string syntax and values." 

66 ) from e 

67 

68 return operation_filters