18
18
import operator
19
19
20
20
21
- def flatten_query_params (obj ):
22
- """Flatten a nested dict into a list of (name,value) tuples.
21
+ def flatten_query_params (obj , strict = False ):
22
+ """Flatten a dict into a list of (name,value) tuples.
23
23
24
24
The result is suitable for setting query params on an http request.
25
25
@@ -28,17 +28,19 @@ def flatten_query_params(obj):
28
28
>>> obj = {'a':
29
29
... {'b':
30
30
... {'c': ['x', 'y', 'z']} },
31
- ... 'd': 'uvw', }
32
- >>> flatten_query_params(obj)
33
- [('a.b.c', 'x'), ('a.b.c', 'y'), ('a.b.c', 'z'), ('d', 'uvw')]
31
+ ... 'd': 'uvw',
32
+ ... 'e': True, }
33
+ >>> flatten_query_params(obj, strict=True)
34
+ [('a.b.c', 'x'), ('a.b.c', 'y'), ('a.b.c', 'z'), ('d', 'uvw'), ('e', 'true')]
34
35
35
36
Note that, as described in
36
37
https://github.com/googleapis/googleapis/blob/48d9fb8c8e287c472af500221c6450ecd45d7d39/google/api/http.proto#L117,
37
38
repeated fields (i.e. list-valued fields) may only contain primitive types (not lists or dicts).
38
39
This is enforced in this function.
39
40
40
41
Args:
41
- obj: a nested dictionary (from json), or None
42
+ obj: a possibly nested dictionary (from json), or None
43
+ strict: a bool, defaulting to False, to enforce that all values in the result tuples be strings and, if boolean, lower-cased.
42
44
43
45
Returns: a list of tuples, with each tuple having a (possibly) multi-part name
44
46
and a scalar value.
@@ -51,17 +53,17 @@ def flatten_query_params(obj):
51
53
if obj is not None and not isinstance (obj , dict ):
52
54
raise TypeError ("flatten_query_params must be called with dict object" )
53
55
54
- return _flatten (obj , key_path = [])
56
+ return _flatten (obj , key_path = [], strict = strict )
55
57
56
58
57
- def _flatten (obj , key_path ):
59
+ def _flatten (obj , key_path , strict = False ):
58
60
if obj is None :
59
61
return []
60
62
if isinstance (obj , dict ):
61
- return _flatten_dict (obj , key_path = key_path )
63
+ return _flatten_dict (obj , key_path = key_path , strict = strict )
62
64
if isinstance (obj , list ):
63
- return _flatten_list (obj , key_path = key_path )
64
- return _flatten_value (obj , key_path = key_path )
65
+ return _flatten_list (obj , key_path = key_path , strict = strict )
66
+ return _flatten_value (obj , key_path = key_path , strict = strict )
65
67
66
68
67
69
def _is_primitive_value (obj ):
@@ -74,21 +76,30 @@ def _is_primitive_value(obj):
74
76
return True
75
77
76
78
77
- def _flatten_value (obj , key_path ):
78
- return [("." .join (key_path ), obj )]
79
+ def _flatten_value (obj , key_path , strict = False ):
80
+ return [("." .join (key_path ), _canonicalize ( obj , strict = strict ) )]
79
81
80
82
81
- def _flatten_dict (obj , key_path ):
82
- items = (_flatten (value , key_path = key_path + [key ]) for key , value in obj .items ())
83
+ def _flatten_dict (obj , key_path , strict = False ):
84
+ items = (_flatten (value , key_path = key_path + [key ], strict = strict ) for key , value in obj .items ())
83
85
return functools .reduce (operator .concat , items , [])
84
86
85
87
86
- def _flatten_list (elems , key_path ):
88
+ def _flatten_list (elems , key_path , strict = False ):
87
89
# Only lists of scalar values are supported.
88
90
# The name (key_path) is repeated for each value.
89
91
items = (
90
- _flatten_value (elem , key_path = key_path )
92
+ _flatten_value (elem , key_path = key_path , strict = strict )
91
93
for elem in elems
92
94
if _is_primitive_value (elem )
93
95
)
94
96
return functools .reduce (operator .concat , items , [])
97
+
98
+
99
+ def _canonicalize (obj , strict = False ):
100
+ if strict :
101
+ value = str (obj )
102
+ if isinstance (obj , bool ):
103
+ value = value .lower ()
104
+ return value
105
+ return obj
0 commit comments