1
1
import ast
2
- from typing import TYPE_CHECKING , Any , List , Optional , Union
2
+ from abc import ABC , abstractmethod
3
+ from typing import TYPE_CHECKING , Any , Generic , List , Optional , TypeVar , Union
3
4
4
5
from dbally .audit .event_tracker import EventTracker
5
6
from dbally .iql import syntax
19
20
if TYPE_CHECKING :
20
21
from dbally .views .structured import ExposedFunction
21
22
23
+ RootT = TypeVar ("RootT" , bound = syntax .Node )
22
24
23
- class IQLProcessor :
25
+
26
+ class IQLProcessor (Generic [RootT ], ABC ):
24
27
"""
25
- Parses IQL string to tree structure .
28
+ Base class for IQL processors .
26
29
"""
27
30
28
31
def __init__ (
@@ -32,9 +35,9 @@ def __init__(
32
35
self .allowed_functions = {func .name : func for func in allowed_functions }
33
36
self ._event_tracker = event_tracker or EventTracker ()
34
37
35
- async def process (self ) -> syntax . Node :
38
+ async def process (self ) -> RootT :
36
39
"""
37
- Process IQL string to root IQL.Node .
40
+ Process IQL string to IQL root node .
38
41
39
42
Returns:
40
43
IQL node which is root of the tree representing IQL query.
@@ -60,25 +63,17 @@ async def process(self) -> syntax.Node:
60
63
61
64
return await self ._parse_node (ast_tree .body [0 ].value )
62
65
63
- async def _parse_node (self , node : Union [ast .expr , ast .Expr ]) -> syntax .Node :
64
- if isinstance (node , ast .BoolOp ):
65
- return await self ._parse_bool_op (node )
66
- if isinstance (node , ast .UnaryOp ) and isinstance (node .op , ast .Not ):
67
- return syntax .Not (await self ._parse_node (node .operand ))
68
- if isinstance (node , ast .Call ):
69
- return await self ._parse_call (node )
70
-
71
- raise IQLUnsupportedSyntaxError (node , self .source )
66
+ @abstractmethod
67
+ async def _parse_node (self , node : Union [ast .expr , ast .Expr ]) -> RootT :
68
+ """
69
+ Parses AST node to IQL node.
72
70
73
- async def _parse_bool_op (self , node : ast .BoolOp ) -> syntax .BoolOp :
74
- if isinstance (node .op , ast .Not ):
75
- return syntax .Not (await self ._parse_node (node .values [0 ]))
76
- if isinstance (node .op , ast .And ):
77
- return syntax .And ([await self ._parse_node (x ) for x in node .values ])
78
- if isinstance (node .op , ast .Or ):
79
- return syntax .Or ([await self ._parse_node (x ) for x in node .values ])
71
+ Args:
72
+ node: AST node to parse.
80
73
81
- raise IQLUnsupportedSyntaxError (node , self .source , context = "BoolOp" )
74
+ Returns:
75
+ IQL node.
76
+ """
82
77
83
78
async def _parse_call (self , node : ast .Call ) -> syntax .FunctionCall :
84
79
func = node .func
@@ -153,3 +148,41 @@ def _to_lower_except_in_quotes(text: str, keywords: List[str]) -> str:
153
148
converted_text = converted_text [: len (converted_text ) - len (keyword )] + keyword .lower ()
154
149
155
150
return converted_text
151
+
152
+
153
+ class IQLFiltersProcessor (IQLProcessor [syntax .Node ]):
154
+ """
155
+ IQL processor for filters.
156
+ """
157
+
158
+ async def _parse_node (self , node : Union [ast .expr , ast .Expr ]) -> syntax .Node :
159
+ if isinstance (node , ast .BoolOp ):
160
+ return await self ._parse_bool_op (node )
161
+ if isinstance (node , ast .UnaryOp ) and isinstance (node .op , ast .Not ):
162
+ return syntax .Not (await self ._parse_node (node .operand ))
163
+ if isinstance (node , ast .Call ):
164
+ return await self ._parse_call (node )
165
+
166
+ raise IQLUnsupportedSyntaxError (node , self .source )
167
+
168
+ async def _parse_bool_op (self , node : ast .BoolOp ) -> syntax .BoolOp :
169
+ if isinstance (node .op , ast .Not ):
170
+ return syntax .Not (await self ._parse_node (node .values [0 ]))
171
+ if isinstance (node .op , ast .And ):
172
+ return syntax .And ([await self ._parse_node (x ) for x in node .values ])
173
+ if isinstance (node .op , ast .Or ):
174
+ return syntax .Or ([await self ._parse_node (x ) for x in node .values ])
175
+
176
+ raise IQLUnsupportedSyntaxError (node , self .source , context = "BoolOp" )
177
+
178
+
179
+ class IQLAggregationProcessor (IQLProcessor [syntax .FunctionCall ]):
180
+ """
181
+ IQL processor for aggregation.
182
+ """
183
+
184
+ async def _parse_node (self , node : Union [ast .expr , ast .Expr ]) -> syntax .FunctionCall :
185
+ if isinstance (node , ast .Call ):
186
+ return await self ._parse_call (node )
187
+
188
+ raise IQLUnsupportedSyntaxError (node , self .source )
0 commit comments