1
+ from abc import ABC , abstractmethod
1
2
from typing import Any , Iterable , Type
2
3
3
4
import jmespath
8
9
from .value_provider import ValueProvider , ValueProviderException
9
10
10
11
12
+ # `QueryStrategy` is here to provide the seam for different optimizations
13
+ # for executing jmespath queries. We can either execute a "fully fledged"
14
+ # jmespath query or we can implement some simple access patterns that
15
+ # are faster to execute. For example, if the expression is a simple key
16
+ # lookup, we can just use the key directly instead of compiling the
17
+ # jmespath expression and then executing it with all the weight and
18
+ # overhead that comes with it.
19
+
20
+
21
+ class QueryStrategy (ABC ):
22
+ @classmethod
23
+ def from_string_expression (cls , expression : str ):
24
+ if expression .isalpha ():
25
+ return KeyLookup (expression )
26
+
27
+ compiled_query = jmespath .compile (expression )
28
+ return ExecuteJmespath (compiled_query )
29
+
30
+ @abstractmethod
31
+ def search (self , context : ProviderContext ):
32
+ pass
33
+
34
+
35
+ class ExecuteJmespath (QueryStrategy ):
36
+ def __init__ (self , compiled_query : ParsedResult ) -> None :
37
+ self .compiled_query = compiled_query
38
+
39
+ def search (self , context : ProviderContext ):
40
+ return self .compiled_query .search (context .document )
41
+
42
+ def __str__ (self ) -> str :
43
+ return str (self .compiled_query .expression )
44
+
45
+
46
+ class KeyLookup (QueryStrategy ):
47
+ def __init__ (self , key : str ) -> None :
48
+ self .key = key
49
+
50
+ def search (self , context : ProviderContext ):
51
+ return context .document .get (self .key , None )
52
+
53
+ def __str__ (self ) -> str :
54
+ return self .key
55
+
56
+
11
57
class JmespathValueProvider (ValueProvider ):
12
58
"""A `ValueProvider` that uses JMESPath to extract values from a document."""
13
59
@@ -24,13 +70,13 @@ def install_yaml_tag(cls, loader: Type[SafeLoader]):
24
70
25
71
@classmethod
26
72
def from_string_expression (cls , expression : str ):
27
- return cls (jmespath . compile (expression ))
73
+ return cls (QueryStrategy . from_string_expression (expression ))
28
74
29
- def __init__ (self , compiled_query : ParsedResult ) -> None :
30
- self .compiled_query = compiled_query
75
+ def __init__ (self , strategy : QueryStrategy ) -> None :
76
+ self .strategy = strategy
31
77
32
78
def search (self , context : ProviderContext ):
33
- raw_search = self .compiled_query .search (context . document )
79
+ raw_search = self .strategy .search (context )
34
80
if raw_search is None :
35
81
return
36
82
if isinstance (raw_search , list ):
@@ -51,14 +97,12 @@ def many_values(self, context: ProviderContext) -> Iterable[Any]:
51
97
raise ValueProviderException (str (context .document ), self ) from e
52
98
53
99
def __str__ (self ) -> str :
54
- return (
55
- f"JmespathValueProvider: { {'expression' : self .compiled_query .expression } } "
56
- )
100
+ return f"JmespathValueProvider: { {'expression' : str (self .strategy )} } "
57
101
58
102
59
103
SafeDumper .add_representer (
60
104
JmespathValueProvider ,
61
105
lambda dumper , jmespath : dumper .represent_scalar (
62
- "!jmespath" , jmespath .compiled_query . expression
106
+ "!jmespath" , str ( jmespath .strategy )
63
107
),
64
108
)
0 commit comments