11import json
22import time
33from contextlib import contextmanager
4- from typing import Generator
4+ from typing import Generator , Literal
55
66
77def _int_keys (dct : dict ) -> dict :
@@ -21,12 +21,20 @@ def recursive_dict_merge(d1: dict, d2: dict) -> dict:
2121 return d1
2222
2323
24- def merge_json_on_disk (dct : dict , file_path : str ) -> None :
24+ def merge_json_on_disk (
25+ dct : dict ,
26+ file_path : str ,
27+ on_non_serializable : Literal ["annotate" , "error" ] = "annotate" ,
28+ ) -> None :
2529 """Merge a dict into a (possibly) existing JSON file.
2630
2731 Args:
2832 file_path (str): Path to JSON file. File will be created if not exist.
2933 dct (dict): Dictionary to merge into JSON file.
34+ on_non_serializable ('annotate' | 'error'): What to do with non-serializable values
35+ encountered in dct. 'annotate' will replace the offending object with a string
36+ indicating the type, e.g. '<not serializable: function>'. 'error' will raise
37+ 'TypeError: Object of type function is not JSON serializable'. Defaults to 'annotate'.
3038 """
3139 try :
3240 with open (file_path ) as json_file :
@@ -36,8 +44,15 @@ def merge_json_on_disk(dct: dict, file_path: str) -> None:
3644 except (FileNotFoundError , json .decoder .JSONDecodeError ): # file missing or empty
3745 pass
3846
47+ def non_serializable_handler (obj : object ) -> str :
48+ # replace functions and classes in dct with string indicating a non-serializable type
49+ return f"<not serializable: { type (obj ).__qualname__ } >"
50+
3951 with open (file_path , "w" ) as file :
40- json .dump (dct , file )
52+ default = (
53+ non_serializable_handler if on_non_serializable == "annotate" else None
54+ )
55+ json .dump (dct , file , default = default )
4156
4257
4358@contextmanager
0 commit comments