1818import operator
1919
2020
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.
2323
2424 The result is suitable for setting query params on an http request.
2525
@@ -28,17 +28,20 @@ def flatten_query_params(obj):
2828 >>> obj = {'a':
2929 ... {'b':
3030 ... {'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')]
3435
3536 Note that, as described in
3637 https://github.com/googleapis/googleapis/blob/48d9fb8c8e287c472af500221c6450ecd45d7d39/google/api/http.proto#L117,
3738 repeated fields (i.e. list-valued fields) may only contain primitive types (not lists or dicts).
3839 This is enforced in this function.
3940
4041 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
44+ result tuples be strings and, if boolean, lower-cased.
4245
4346 Returns: a list of tuples, with each tuple having a (possibly) multi-part name
4447 and a scalar value.
@@ -51,17 +54,17 @@ def flatten_query_params(obj):
5154 if obj is not None and not isinstance (obj , dict ):
5255 raise TypeError ("flatten_query_params must be called with dict object" )
5356
54- return _flatten (obj , key_path = [])
57+ return _flatten (obj , key_path = [], strict = strict )
5558
5659
57- def _flatten (obj , key_path ):
60+ def _flatten (obj , key_path , strict = False ):
5861 if obj is None :
5962 return []
6063 if isinstance (obj , dict ):
61- return _flatten_dict (obj , key_path = key_path )
64+ return _flatten_dict (obj , key_path = key_path , strict = strict )
6265 if isinstance (obj , list ):
63- return _flatten_list (obj , key_path = key_path )
64- return _flatten_value (obj , key_path = key_path )
66+ return _flatten_list (obj , key_path = key_path , strict = strict )
67+ return _flatten_value (obj , key_path = key_path , strict = strict )
6568
6669
6770def _is_primitive_value (obj ):
@@ -74,21 +77,33 @@ def _is_primitive_value(obj):
7477 return True
7578
7679
77- def _flatten_value (obj , key_path ):
78- return [("." .join (key_path ), obj )]
80+ def _flatten_value (obj , key_path , strict = False ):
81+ return [("." .join (key_path ), _canonicalize ( obj , strict = strict ) )]
7982
8083
81- def _flatten_dict (obj , key_path ):
82- items = (_flatten (value , key_path = key_path + [key ]) for key , value in obj .items ())
84+ def _flatten_dict (obj , key_path , strict = False ):
85+ items = (
86+ _flatten (value , key_path = key_path + [key ], strict = strict )
87+ for key , value in obj .items ()
88+ )
8389 return functools .reduce (operator .concat , items , [])
8490
8591
86- def _flatten_list (elems , key_path ):
92+ def _flatten_list (elems , key_path , strict = False ):
8793 # Only lists of scalar values are supported.
8894 # The name (key_path) is repeated for each value.
8995 items = (
90- _flatten_value (elem , key_path = key_path )
96+ _flatten_value (elem , key_path = key_path , strict = strict )
9197 for elem in elems
9298 if _is_primitive_value (elem )
9399 )
94100 return functools .reduce (operator .concat , items , [])
101+
102+
103+ def _canonicalize (obj , strict = False ):
104+ if strict :
105+ value = str (obj )
106+ if isinstance (obj , bool ):
107+ value = value .lower ()
108+ return value
109+ return obj
0 commit comments