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