@@ -160,7 +160,8 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
160160
161161 @staticmethod
162162 def trim_iterable (untrimmed ,
163- to_json = False ):
163+ to_json = False ,
164+ context : str = None ):
164165 """Convert any :class:`EnforcedNullType` values in ``untrimmed`` to ``'null'``.
165166
166167 :param untrimmed: The iterable whose members may still be
@@ -170,6 +171,11 @@ def trim_iterable(untrimmed,
170171 :param to_json: If ``True``, will remove all members from ``untrimmed`` that are
171172 not serializable to JSON. Defaults to ``False``.
172173 :type to_json: :class:`bool <python:bool>`
174+
175+ :param context: If provided, will inform the method of the context in which it is
176+ being run which may inform special handling cases (e.g. where empty strings may
177+ be important / allowable). Defaults to :obj:`None <python:None>`.
178+ :type context: :class:`str <python:str>` or :obj:`None <python:None>`
173179
174180 :rtype: iterable
175181 """
@@ -183,24 +189,32 @@ def trim_iterable(untrimmed,
183189 elif item is None or item == constants .EnforcedNull :
184190 trimmed .append ('null' )
185191 elif hasattr (item , 'trim_dict' ):
192+ updated_context = item .__class__ .__name__
186193 untrimmed_item = item ._to_untrimmed_dict ()
187- item_as_dict = HighchartsMeta .trim_dict (untrimmed_item , to_json = to_json )
194+ item_as_dict = HighchartsMeta .trim_dict (untrimmed_item ,
195+ to_json = to_json ,
196+ context = updated_context )
188197 if item_as_dict :
189198 trimmed .append (item_as_dict )
190199 elif isinstance (item , dict ):
191200 if item :
192- trimmed .append (HighchartsMeta .trim_dict (item , to_json = to_json ))
201+ trimmed .append (HighchartsMeta .trim_dict (item ,
202+ to_json = to_json ,
203+ context = context ))
193204 elif checkers .is_iterable (item , forbid_literals = (str , bytes , dict )):
194205 if item :
195- trimmed .append (HighchartsMeta .trim_iterable (item , to_json = to_json ))
206+ trimmed .append (HighchartsMeta .trim_iterable (item ,
207+ to_json = to_json ,
208+ context = context ))
196209 else :
197210 trimmed .append (item )
198211
199212 return trimmed
200213
201214 @staticmethod
202215 def trim_dict (untrimmed : dict ,
203- to_json : bool = False ) -> dict :
216+ to_json : bool = False ,
217+ context : str = None ) -> dict :
204218 """Remove keys from ``untrimmed`` whose values are :obj:`None <python:None>` and
205219 convert values that have ``.to_dict()`` methods.
206220
@@ -211,12 +225,18 @@ def trim_dict(untrimmed: dict,
211225 :param to_json: If ``True``, will remove all keys from ``untrimmed`` that are not
212226 serializable to JSON. Defaults to ``False``.
213227 :type to_json: :class:`bool <python:bool>`
228+
229+ :param context: If provided, will inform the method of the context in which it is
230+ being run which may inform special handling cases (e.g. where empty strings may
231+ be important / allowable). Defaults to :obj:`None <python:None>`.
232+ :type context: :class:`str <python:str>` or :obj:`None <python:None>`
214233
215234 :returns: Trimmed :class:`dict <python:dict>`
216235 :rtype: :class:`dict <python:dict>`
217236 """
218237 as_dict = {}
219238 for key in untrimmed :
239+ context_key = f'{ context } .{ key } '
220240 value = untrimmed .get (key , None )
221241 # bool -> Boolean
222242 if isinstance (value , bool ):
@@ -227,8 +247,10 @@ def trim_dict(untrimmed: dict,
227247 # HighchartsMeta -> dict --> object
228248 elif value and hasattr (value , '_to_untrimmed_dict' ):
229249 untrimmed_value = value ._to_untrimmed_dict ()
250+ updated_context = value .__class__ .__name__
230251 trimmed_value = HighchartsMeta .trim_dict (untrimmed_value ,
231- to_json = to_json )
252+ to_json = to_json ,
253+ context = updated_context )
232254 if trimmed_value :
233255 as_dict [key ] = trimmed_value
234256 # Enforced null
@@ -237,25 +259,33 @@ def trim_dict(untrimmed: dict,
237259 # dict -> object
238260 elif isinstance (value , dict ):
239261 trimmed_value = HighchartsMeta .trim_dict (value ,
240- to_json = to_json )
262+ to_json = to_json ,
263+ context = context )
241264 if trimmed_value :
242265 as_dict [key ] = trimmed_value
243266 # iterable -> array
244267 elif checkers .is_iterable (value , forbid_literals = (str , bytes , dict )):
245- trimmed_value = HighchartsMeta .trim_iterable (value , to_json = to_json )
268+ trimmed_value = HighchartsMeta .trim_iterable (value ,
269+ to_json = to_json ,
270+ context = context )
246271 if trimmed_value :
247272 as_dict [key ] = trimmed_value
248273 # Pandas Timestamp
249274 elif checkers .is_type (value , 'Timestamp' ):
250275 as_dict [key ] = value .timestamp ()
251276 # other truthy -> str / number
252277 elif value :
253- trimmed_value = HighchartsMeta .trim_iterable (value , to_json = to_json )
278+ trimmed_value = HighchartsMeta .trim_iterable (value ,
279+ to_json = to_json ,
280+ context = context )
254281 if trimmed_value :
255282 as_dict [key ] = trimmed_value
256283 # other falsy -> str / number
257284 elif value in [0 , 0. , False ]:
258285 as_dict [key ] = value
286+ # other falsy -> str, but empty string is allowed
287+ elif value == '' and context_key in constants .EMPTY_STRING_CONTEXTS :
288+ as_dict [key ] = ''
259289
260290 return as_dict
261291
@@ -353,7 +383,8 @@ def to_dict(self) -> dict:
353383 """
354384 untrimmed = self ._to_untrimmed_dict ()
355385
356- return self .trim_dict (untrimmed )
386+ return self .trim_dict (untrimmed ,
387+ context = self .__class__ .__name__ )
357388
358389 def to_json (self ,
359390 filename = None ,
@@ -386,7 +417,9 @@ def to_json(self,
386417
387418 untrimmed = self ._to_untrimmed_dict ()
388419
389- as_dict = self .trim_dict (untrimmed , to_json = True )
420+ as_dict = self .trim_dict (untrimmed ,
421+ to_json = True ,
422+ context = self .__class__ .__name__ )
390423
391424 for key in as_dict :
392425 if as_dict [key ] == constants .EnforcedNull or as_dict [key ] == 'null' :
0 commit comments