abstract-utilities 0.2.2.480__py3-none-any.whl → 0.2.2.688__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of abstract-utilities might be problematic. Click here for more details.

Files changed (215) hide show
  1. abstract_utilities/__init__.py +24 -16
  2. abstract_utilities/circular_import_finder.py +222 -0
  3. abstract_utilities/circular_import_finder2.py +118 -0
  4. abstract_utilities/class_utils/__init__.py +7 -0
  5. abstract_utilities/class_utils/abstract_classes.py +144 -0
  6. abstract_utilities/class_utils/caller_utils.py +92 -0
  7. abstract_utilities/class_utils/class_utils.py +109 -0
  8. abstract_utilities/class_utils/function_utils.py +153 -0
  9. abstract_utilities/class_utils/global_utils.py +71 -0
  10. abstract_utilities/class_utils/imports/__init__.py +2 -0
  11. abstract_utilities/class_utils/imports/imports.py +2 -0
  12. abstract_utilities/class_utils/imports/utils.py +40 -0
  13. abstract_utilities/class_utils/module_utils.py +63 -0
  14. abstract_utilities/directory_utils/__init__.py +2 -0
  15. abstract_utilities/directory_utils/directory_utils.py +94 -0
  16. abstract_utilities/directory_utils/imports/__init__.py +2 -0
  17. abstract_utilities/directory_utils/imports/imports.py +1 -0
  18. abstract_utilities/directory_utils/imports/module_imports.py +2 -0
  19. abstract_utilities/directory_utils/name_utils.py +43 -0
  20. abstract_utilities/directory_utils/size_utils.py +57 -0
  21. abstract_utilities/directory_utils/src/__init__.py +4 -0
  22. abstract_utilities/directory_utils/src/directory_utils.py +110 -0
  23. abstract_utilities/directory_utils/src/name_utils.py +43 -0
  24. abstract_utilities/directory_utils/src/size_utils.py +57 -0
  25. abstract_utilities/directory_utils/src/utils.py +116 -0
  26. abstract_utilities/directory_utils/utils.py +116 -0
  27. abstract_utilities/env_utils/imports/imports.py +5 -3
  28. abstract_utilities/error_utils/__init__.py +2 -0
  29. abstract_utilities/error_utils/error_utils.py +25 -0
  30. abstract_utilities/error_utils/imports/__init__.py +2 -0
  31. abstract_utilities/error_utils/imports/imports.py +1 -0
  32. abstract_utilities/error_utils/imports/module_imports.py +1 -0
  33. abstract_utilities/file_utils/__init__.py +1 -2
  34. abstract_utilities/file_utils/file_utils/type_checks.py +2 -1
  35. abstract_utilities/file_utils/imports/classes.py +59 -55
  36. abstract_utilities/file_utils/imports/constants.py +84 -4
  37. abstract_utilities/file_utils/imports/imports.py +2 -21
  38. abstract_utilities/file_utils/imports/module_imports.py +3 -8
  39. abstract_utilities/file_utils/module_imports.py +12 -0
  40. abstract_utilities/file_utils/src/__init__.py +7 -0
  41. abstract_utilities/file_utils/src/file_filters/__init__.py +1 -0
  42. abstract_utilities/file_utils/src/file_filters/ensure_utils.py +490 -0
  43. abstract_utilities/file_utils/src/file_filters/filter_params.py +150 -0
  44. abstract_utilities/file_utils/src/file_filters/filter_utils.py +78 -0
  45. abstract_utilities/file_utils/src/file_filters/predicate_utils.py +44 -0
  46. abstract_utilities/file_utils/src/file_filters.py +177 -0
  47. abstract_utilities/file_utils/src/file_reader.py +543 -0
  48. abstract_utilities/file_utils/src/file_utils.py +156 -0
  49. abstract_utilities/file_utils/src/filter_params.py +197 -0
  50. abstract_utilities/file_utils/src/find_collect.py +200 -0
  51. abstract_utilities/file_utils/src/find_content.py +210 -0
  52. abstract_utilities/file_utils/src/initFunctionsGen.py +293 -0
  53. abstract_utilities/file_utils/src/initFunctionsGens.py +280 -0
  54. abstract_utilities/file_utils/src/map_utils.py +29 -0
  55. abstract_utilities/file_utils/src/pdf_utils.py +300 -0
  56. abstract_utilities/file_utils/src/reader_utils/__init__.py +4 -0
  57. abstract_utilities/file_utils/src/reader_utils/directory_reader.py +53 -0
  58. abstract_utilities/file_utils/src/reader_utils/file_reader.py +543 -0
  59. abstract_utilities/file_utils/src/reader_utils/file_readers.py +376 -0
  60. abstract_utilities/file_utils/src/reader_utils/imports.py +18 -0
  61. abstract_utilities/file_utils/src/reader_utils/pdf_utils.py +300 -0
  62. abstract_utilities/file_utils/src/type_checks.py +91 -0
  63. abstract_utilities/file_utils (2)/__init__.py +2 -0
  64. abstract_utilities/file_utils (2)/imports/__init__.py +2 -0
  65. abstract_utilities/file_utils (2)/imports/constants.py +118 -0
  66. abstract_utilities/file_utils (2)/imports/imports/__init__.py +3 -0
  67. abstract_utilities/file_utils (2)/imports/imports/constants.py +119 -0
  68. abstract_utilities/file_utils (2)/imports/imports/imports.py +46 -0
  69. abstract_utilities/file_utils (2)/imports/imports/module_imports.py +8 -0
  70. abstract_utilities/file_utils (2)/imports/utils/__init__.py +3 -0
  71. abstract_utilities/file_utils (2)/imports/utils/classes.py +379 -0
  72. abstract_utilities/file_utils (2)/imports/utils/clean_imps.py +155 -0
  73. abstract_utilities/file_utils (2)/imports/utils/filter_utils.py +341 -0
  74. abstract_utilities/file_utils (2)/src/__init__.py +8 -0
  75. abstract_utilities/file_utils (2)/src/file_filters.py +155 -0
  76. abstract_utilities/file_utils (2)/src/file_reader.py +604 -0
  77. abstract_utilities/file_utils (2)/src/find_collect.py +258 -0
  78. abstract_utilities/file_utils (2)/src/initFunctionsGen.py +286 -0
  79. abstract_utilities/file_utils (2)/src/map_utils.py +28 -0
  80. abstract_utilities/file_utils (2)/src/pdf_utils.py +300 -0
  81. abstract_utilities/hash_utils/__init__.py +2 -0
  82. abstract_utilities/hash_utils/hash_utils.py +5 -0
  83. abstract_utilities/hash_utils/imports/__init__.py +2 -0
  84. abstract_utilities/hash_utils/imports/imports.py +1 -0
  85. abstract_utilities/hash_utils/imports/module_imports.py +0 -0
  86. abstract_utilities/history_utils/__init__.py +2 -0
  87. abstract_utilities/history_utils/history_utils.py +37 -0
  88. abstract_utilities/history_utils/imports/__init__.py +2 -0
  89. abstract_utilities/history_utils/imports/imports.py +1 -0
  90. abstract_utilities/history_utils/imports/module_imports.py +0 -0
  91. abstract_utilities/import_utils/__init__.py +2 -0
  92. abstract_utilities/import_utils/circular_import_finder.py +222 -0
  93. abstract_utilities/import_utils/circular_import_finder2.py +118 -0
  94. abstract_utilities/import_utils/imports/__init__.py +4 -0
  95. abstract_utilities/import_utils/imports/constants.py +2 -0
  96. abstract_utilities/import_utils/imports/imports.py +4 -0
  97. abstract_utilities/import_utils/imports/init_imports.py +3 -0
  98. abstract_utilities/import_utils/imports/module_imports.py +9 -0
  99. abstract_utilities/import_utils/imports/utils.py +30 -0
  100. abstract_utilities/import_utils/src/__init__.py +8 -0
  101. abstract_utilities/import_utils/src/clean_imports.py +278 -0
  102. abstract_utilities/import_utils/src/dot_utils.py +80 -0
  103. abstract_utilities/import_utils/src/extract_utils.py +46 -0
  104. abstract_utilities/import_utils/src/import_functions.py +110 -0
  105. abstract_utilities/import_utils/src/import_utils.py +349 -0
  106. abstract_utilities/import_utils/src/layze_import_utils/__init__.py +2 -0
  107. abstract_utilities/import_utils/src/layze_import_utils/lazy_utils.py +41 -0
  108. abstract_utilities/import_utils/src/layze_import_utils/nullProxy.py +37 -0
  109. abstract_utilities/import_utils/src/nullProxy.py +30 -0
  110. abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
  111. abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
  112. abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
  113. abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
  114. abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
  115. abstract_utilities/import_utils/src/package_utils.py +140 -0
  116. abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
  117. abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
  118. abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
  119. abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
  120. abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
  121. abstract_utilities/import_utils/src/pkg_utils.py +194 -0
  122. abstract_utilities/import_utils/src/sysroot_utils.py +112 -0
  123. abstract_utilities/imports.py +21 -0
  124. abstract_utilities/json_utils/__init__.py +2 -0
  125. abstract_utilities/json_utils/imports/__init__.py +2 -0
  126. abstract_utilities/json_utils/imports/imports.py +2 -0
  127. abstract_utilities/json_utils/imports/module_imports.py +5 -0
  128. abstract_utilities/json_utils/json_utils.py +777 -0
  129. abstract_utilities/list_utils/__init__.py +2 -0
  130. abstract_utilities/list_utils/imports/__init__.py +2 -0
  131. abstract_utilities/list_utils/imports/imports.py +1 -0
  132. abstract_utilities/list_utils/imports/module_imports.py +0 -0
  133. abstract_utilities/list_utils/list_utils.py +202 -0
  134. abstract_utilities/log_utils/__init__.py +5 -0
  135. abstract_utilities/log_utils/abstractLogManager.py +64 -0
  136. abstract_utilities/log_utils/call_response.py +68 -0
  137. abstract_utilities/log_utils/imports/__init__.py +2 -0
  138. abstract_utilities/log_utils/imports/imports.py +7 -0
  139. abstract_utilities/log_utils/imports/module_imports.py +2 -0
  140. abstract_utilities/log_utils/log_file.py +162 -0
  141. abstract_utilities/log_utils/logger_callable.py +49 -0
  142. abstract_utilities/math_utils/__init__.py +2 -0
  143. abstract_utilities/math_utils/imports/__init__.py +2 -0
  144. abstract_utilities/math_utils/imports/imports.py +2 -0
  145. abstract_utilities/math_utils/imports/module_imports.py +1 -0
  146. abstract_utilities/math_utils/math_utils.py +208 -0
  147. abstract_utilities/parse_utils/__init__.py +2 -0
  148. abstract_utilities/parse_utils/imports/__init__.py +3 -0
  149. abstract_utilities/parse_utils/imports/constants.py +10 -0
  150. abstract_utilities/parse_utils/imports/imports.py +2 -0
  151. abstract_utilities/parse_utils/imports/module_imports.py +4 -0
  152. abstract_utilities/parse_utils/parse_utils.py +539 -0
  153. abstract_utilities/path_utils/__init__.py +2 -0
  154. abstract_utilities/path_utils/imports/__init__.py +3 -0
  155. abstract_utilities/path_utils/imports/imports.py +1 -0
  156. abstract_utilities/path_utils/imports/module_imports.py +8 -0
  157. abstract_utilities/path_utils/path_utils.py +248 -0
  158. abstract_utilities/path_utils.py +95 -14
  159. abstract_utilities/read_write_utils/__init__.py +1 -0
  160. abstract_utilities/read_write_utils/imports/__init__.py +2 -0
  161. abstract_utilities/read_write_utils/imports/imports.py +2 -0
  162. abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
  163. abstract_utilities/read_write_utils/read_write_utils.py +439 -0
  164. abstract_utilities/read_write_utils.py +113 -62
  165. abstract_utilities/safe_utils/__init__.py +2 -0
  166. abstract_utilities/safe_utils/imports/__init__.py +3 -0
  167. abstract_utilities/safe_utils/imports/imports.py +2 -0
  168. abstract_utilities/safe_utils/imports/module_imports.py +2 -0
  169. abstract_utilities/safe_utils/safe_utils.py +166 -0
  170. abstract_utilities/ssh_utils/__init__.py +3 -1
  171. abstract_utilities/ssh_utils/classes.py +0 -1
  172. abstract_utilities/ssh_utils/cmd_utils.py +207 -0
  173. abstract_utilities/ssh_utils/imports/__init__.py +3 -0
  174. abstract_utilities/ssh_utils/imports/imports.py +5 -0
  175. abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
  176. abstract_utilities/ssh_utils/imports/utils.py +189 -0
  177. abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
  178. abstract_utilities/ssh_utils/type_checks.py +92 -0
  179. abstract_utilities/string_utils/__init__.py +4 -0
  180. abstract_utilities/string_utils/clean_utils.py +28 -0
  181. abstract_utilities/string_utils/eat_utils.py +103 -0
  182. abstract_utilities/string_utils/imports/__init__.py +3 -0
  183. abstract_utilities/string_utils/imports/imports.py +2 -0
  184. abstract_utilities/string_utils/imports/module_imports.py +2 -0
  185. abstract_utilities/string_utils/imports/utils.py +81 -0
  186. abstract_utilities/string_utils/replace_utils.py +27 -0
  187. abstract_utilities/string_utils.py +1 -1
  188. abstract_utilities/thread_utils/__init__.py +2 -0
  189. abstract_utilities/thread_utils/imports/__init__.py +2 -0
  190. abstract_utilities/thread_utils/imports/imports.py +2 -0
  191. abstract_utilities/thread_utils/imports/module_imports.py +2 -0
  192. abstract_utilities/thread_utils/thread_utils.py +140 -0
  193. abstract_utilities/time_utils/__init__.py +2 -0
  194. abstract_utilities/time_utils/imports/__init__.py +2 -0
  195. abstract_utilities/time_utils/imports/imports.py +3 -0
  196. abstract_utilities/time_utils/imports/module_imports.py +1 -0
  197. abstract_utilities/time_utils/time_utils.py +392 -0
  198. abstract_utilities/type_utils/__init__.py +7 -0
  199. abstract_utilities/type_utils/alpha_utils.py +59 -0
  200. abstract_utilities/type_utils/get_type.py +120 -0
  201. abstract_utilities/type_utils/imports/__init__.py +3 -0
  202. abstract_utilities/type_utils/imports/constants.py +134 -0
  203. abstract_utilities/type_utils/imports/imports.py +4 -0
  204. abstract_utilities/type_utils/imports/module_imports.py +25 -0
  205. abstract_utilities/type_utils/is_type.py +455 -0
  206. abstract_utilities/type_utils/make_type.py +126 -0
  207. abstract_utilities/type_utils/mime_types.py +68 -0
  208. abstract_utilities/type_utils/num_utils.py +19 -0
  209. abstract_utilities/type_utils/type_utils.py +104 -0
  210. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/METADATA +1 -1
  211. abstract_utilities-0.2.2.688.dist-info/RECORD +288 -0
  212. imports/__init__.py +36 -0
  213. abstract_utilities-0.2.2.480.dist-info/RECORD +0 -92
  214. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/WHEEL +0 -0
  215. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,777 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ json_utils.py
4
+
5
+ This script is a utility module providing functions for handling JSON data. It includes functionalities like:
6
+ 1. Converting JSON strings to dictionaries and vice versa.
7
+ 2. Merging, adding to, updating, and removing keys from dictionaries.
8
+ 3. Retrieving keys, values, specific items, and key-value pairs from dictionaries.
9
+ 4. Recursively displaying values of nested JSON data structures with indentation.
10
+ 5. Loading from and saving dictionaries to JSON files.
11
+ 6. Validating and cleaning up JSON strings.
12
+ 7. Searching and modifying nested JSON structures based on specific keys, values, or paths.
13
+ 8. Inverting JSON data structures.
14
+ 9. Creating and reading from JSON files.
15
+
16
+ Each function is documented with Python docstrings for detailed usage instructions.
17
+
18
+ This module is part of the `abstract_utilities` package.
19
+
20
+ Author: putkoff
21
+ Date: 05/31/2023
22
+ Version: 0.1.2
23
+ """
24
+
25
+ from .imports import *
26
+
27
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
28
+ logger = logging.getLogger(__name__)
29
+ def get_keys(mapping,typ=None):
30
+ typ = typ or set
31
+ if isinstance(mapping,dict):
32
+ mapping = mapping.keys()
33
+ return typ(mapping)
34
+ def make_key_map(dict_obj):
35
+ return {k:get_keys(v) for k,v in dict_obj.items()}
36
+ def convert_and_normalize_values(values):
37
+ for value in values:
38
+ if isinstance(value, str):
39
+ yield value.lower()
40
+ elif isinstance(value, (int, float)):
41
+ yield value
42
+ else:
43
+ yield str(value).lower()
44
+ def json_key_or_default(json_data,key,default_value):
45
+ json_data = safe_json_loads(json_data)
46
+ if not isinstance(json_data,dict) or (isinstance(json_data,dict) and key not in json_data):
47
+ return default_value
48
+ return json_data[key]
49
+
50
+
51
+
52
+ def is_valid_json(json_string: str) -> bool:
53
+ """
54
+ Checks whether a given string is a valid JSON string.
55
+
56
+ Args:
57
+ json_string (str): The string to check.
58
+
59
+ Returns:
60
+ bool: True if the string is valid JSON, False otherwise.
61
+ """
62
+ try:
63
+ json_obj = json.loads(json_string)
64
+ return True
65
+ except json.JSONDecodeError:
66
+ return False
67
+ def get_error_msg(error_msg, default_error_msg):
68
+ return error_msg if error_msg else default_error_msg
69
+ def validate_file_path(file_path,is_read=False):
70
+ if file_path and isinstance(file_path,str):
71
+ if os.path.isfile(file_path) or os.path.isdir(file_path):
72
+ return file_path
73
+ if not is_read:
74
+ dirname = os.path.dirname(file_path)
75
+ if os.path.isdir(dirname):
76
+ return file_path
77
+ def get_file_path(*args,is_read=False,**kwargs):
78
+ args = list(args)
79
+ for file_path in args:
80
+ if validate_file_path(file_path,is_read=is_read):
81
+ return file_path
82
+ for file_path in list(kwargs.values()):
83
+ if validate_file_path(file_path,is_read=is_read):
84
+ return file_path
85
+ def write_file(data,file_path):
86
+ with open(file_path, 'w', encoding='utf-8') as file:
87
+ file.write(str(data))
88
+ def write_json(data,file_path, ensure_ascii=False, indent=4):
89
+ with open(file_path, 'w', encoding='utf-8') as file:
90
+ json.dump(data, file, ensure_ascii=ensure_ascii, indent=indent)
91
+ def safe_write_json(data,file_path, ensure_ascii=False, indent=4):
92
+ if isinstance(data, (dict, list, tuple)):
93
+ write_json(data,file_path, ensure_ascii=ensure_ascii, indent=indent)
94
+ else:
95
+ write_file(data,file_path)
96
+ def read_json(file_path):
97
+ with open(file_path, 'r', encoding='utf-8') as file:
98
+ return json.load(file)
99
+ def output_read_write_error(e,function_name,file_path,valid_file_path=None,data=None,is_read=False):
100
+ error_text = f"Error in {function_name};{e}\nFile path: {file_path} "
101
+
102
+ if valid_file_path == None:
103
+ error_text+=f"\nValid File path: {valid_file_path} "
104
+
105
+ if not is_read:
106
+ error_text+=f"\nData: {data} "
107
+ logger.error(error_text)
108
+ def safe_dump_to_file(data, file_path=None, ensure_ascii=False, indent=4, *args, **kwargs):
109
+ is_read=False
110
+ file_args = [file_path,data]
111
+ valid_file_path = get_file_path(*file_args,*args,is_read=is_read,**kwargs)
112
+
113
+ if valid_file_path:
114
+ file_path = valid_file_path
115
+ if file_path == file_args[-1]:
116
+ data = file_args[0]
117
+ if file_path is not None and data is not None:
118
+ try:
119
+ safe_write_json(data,file_path, ensure_ascii=ensure_ascii, indent=indent)
120
+ except Exception as e:
121
+ function_name='safe_dump_to_file'
122
+ output_read_write_error(e,function_name,file_path,valid_file_path,is_read=is_read)
123
+ else:
124
+ logger.error("file_path and data must be provided to safe_dump_to_file")
125
+
126
+ def safe_read_from_json(file_path,*args,**kwargs):
127
+ is_read=True
128
+
129
+
130
+ valid_file_path = get_file_path(file_path,*args,is_read=is_read,**kwargs)
131
+ if valid_file_path:
132
+ file_path = valid_file_path
133
+ try:
134
+ return read_json(file_path)
135
+ except Exception as e:
136
+ function_name='safe_read_from_json'
137
+ output_read_write_error(e,function_name,file_path,valid_file_path,is_read=is_read)
138
+ return None
139
+
140
+ def create_and_read_json(*args, **kwargs) -> dict:
141
+ """
142
+ Create a JSON file if it does not exist, then read from it.
143
+
144
+ Args:
145
+ file_path (str): The path of the file to create and read from.
146
+ json_data (dict): The content to write to the file if it does not exist.
147
+
148
+ Returns:
149
+ dict: The contents of the JSON file.
150
+ """
151
+ is_read=True
152
+ valid_file_path = get_file_path(*args,is_read=is_read,**kwargs)
153
+ if not valid_file_path:
154
+ safe_dump_to_file(*args, **kwargs)
155
+ return safe_read_from_json(*args, **kwargs)
156
+ def read_from_json(*args, **kwargs):
157
+ return safe_read_from_json(*args, **kwargs)
158
+ def safe_load_from_json(*args, **kwargs):
159
+ return safe_read_from_json(*args, **kwargs)
160
+ def safe_load_from_file(*args, **kwargs):
161
+ return safe_read_from_json(*args, **kwargs)
162
+ def safe_read_from_file(*args, **kwargs):
163
+ return safe_read_from_json(*args, **kwargs)
164
+ def safe_json_reads(*args, **kwargs):
165
+ return safe_read_from_json(*args, **kwargs)
166
+
167
+ def safe_dump_to_json(*args, **kwargs):
168
+ return safe_dump_to_file(*args, **kwargs)
169
+ def safe_write_to_json(*args, **kwargs):
170
+ return safe_dump_to_file(*args, **kwargs)
171
+ def safe_write_to_file(*args, **kwargs):
172
+ return safe_dump_to_file(*args, **kwargs)
173
+
174
+
175
+
176
+ def find_keys(data, target_keys):
177
+ def _find_keys_recursive(data, target_keys, values):
178
+ if isinstance(data, dict):
179
+ for key, value in data.items():
180
+ if key in target_keys:
181
+ values.append(value)
182
+ _find_keys_recursive(value, target_keys, values)
183
+ elif isinstance(data, list):
184
+ for item in data:
185
+ _find_keys_recursive(item, target_keys, values)
186
+
187
+ values = []
188
+ _find_keys_recursive(data, target_keys, values)
189
+ return values
190
+ def try_json_dumps_spec(obj, logger=True, level='error', file_path=None, **kwargs):
191
+ """
192
+ Attempts to serialize an object to JSON using json.dumps or json.dump.
193
+
194
+ Args:
195
+ obj: The Python object to serialize (e.g., dict, list, str, int, etc.).
196
+ logger: Logger object or None to use _default_logger.
197
+ level: Logging level for errors (default: 'error').
198
+ file_path: If provided, writes JSON to this file using json.dump.
199
+ **kwargs: Additional arguments to pass to json.dumps or json.dump (e.g., indent, sort_keys).
200
+
201
+ Returns:
202
+ str: The JSON-serialized string if file_path is None and serialization succeeds.
203
+ None: If serialization fails or file_path is provided (in which case it writes to the file).
204
+
205
+ Raises:
206
+ ValueError: If file_path is provided but the file cannot be written.
207
+ """
208
+
209
+ try:
210
+ if file_path:
211
+ # Use json.dump to write to a file
212
+ with open(file_path, 'w', encoding='utf-8') as f:
213
+ json.dump(obj, f, **kwargs)
214
+ return None
215
+ else:
216
+ # Use json.dumps to return a string
217
+ return json.dumps(obj)
218
+ except (TypeError, OverflowError, ValueError) as e:
219
+ if log_callable:
220
+ print_or_log(f"Exception in json.dumps/dump: {e}")
221
+ return None
222
+ def run_it(endpoint,**kwargs):
223
+ response= make_request_link('typicaly',endpoint,data=kwargs)
224
+ return response
225
+ def get_logNone(e):
226
+ logger(f"{e}")
227
+ return None
228
+ def try_json_loads(data):
229
+ try:
230
+ data = json.loads(data)
231
+ except Exception as e:
232
+ data = None#get_logNone(e)
233
+ return data
234
+ def try_json_load(file):
235
+ try:
236
+ file = json.load(file)
237
+ except Exception as e:
238
+ file = get_logNone(e)
239
+ return file
240
+ def try_json_dump(file):
241
+ try:
242
+ file = json.dump(file)
243
+ except Exception as e:
244
+ file = get_logNone(e)
245
+ return file
246
+ def try_json_dumps(data):
247
+ try:
248
+ data = json.dumps(data)
249
+ except Exception as e:
250
+ data = get_logNone(e)
251
+ return data
252
+ def safe_json_loads(data):
253
+ if not isinstance(data,dict):
254
+ data = try_json_loads(data) or data
255
+ return data
256
+ def safe_json_load(file):
257
+ file = try_json_load(file) or file
258
+ return file
259
+ def safe_json_dump(file):
260
+ file = try_json_dump(file) or file
261
+ return file
262
+ def safe_json_dumps(data):
263
+ data = try_json_dumps(data) or data
264
+ return data
265
+ def unified_json_loader(file_path, default_value=None, encoding='utf-8'):
266
+ # Try to load from the file
267
+ with open(file_path, 'r', encoding=encoding) as file:
268
+ content = all_try(data=file, function=try_json_load, error_value=json.JSONDecodeError, error=False)
269
+
270
+ if isinstance(content, dict):
271
+ return content
272
+
273
+ # Try to load from the file as a string
274
+ with open(file_path, 'r', encoding=encoding) as file:
275
+ content_str = file.read()
276
+ content = all_try(data=content_str, function=try_json_loads, error_value=json.JSONDecodeError, error=False)
277
+
278
+ if isinstance(content, dict):
279
+ return content
280
+
281
+ print(f"Error reading JSON from '{file_path}'.")
282
+ return default_value
283
+
284
+
285
+ def get_key_values_from_path(json_data, path):
286
+ try_path = get_value_from_path(json_data, path[:-1])
287
+ if isinstance(try_path, dict):
288
+ return list(try_path.keys())
289
+
290
+ current_data = json_data
291
+ for step in path:
292
+ try:
293
+ current_data = current_data[step]
294
+ if isinstance(current_data, str):
295
+ try:
296
+ current_data = json.loads(current_data)
297
+ except json.JSONDecodeError:
298
+ pass
299
+ except (TypeError, KeyError, IndexError):
300
+ return None
301
+
302
+ if isinstance(current_data, dict):
303
+ return list(current_data.keys())
304
+ else:
305
+ return None
306
+ def convert_to_json(obj):
307
+ if isinstance(obj, dict):
308
+ return obj
309
+ if isinstance(obj, str):
310
+ return safe_json_loads(obj)
311
+ return None
312
+ def get_any_key(data,key):
313
+ path_to_key = find_paths_to_key(safe_json_loads(data),key)
314
+ if path_to_key:
315
+ value = safe_json_loads(data)
316
+ for each in path_to_key[0]:
317
+ value = safe_json_loads(value[each])
318
+ return value
319
+ return path_to_key
320
+
321
+ def all_try(function=None, data=None, var_data=None, error=False, error_msg=None, error_value=Exception, attach=None, attach_var_data=None):
322
+ try:
323
+ if not function:
324
+ raise ValueError("Function is required")
325
+
326
+ if var_data and not data:
327
+ result = function(**var_data)
328
+ elif data and not var_data:
329
+ if attach and attach_var_data:
330
+ result = function(data).attach(**attach_var_data)
331
+ else:
332
+ result = function(data).attach() if attach else function(data)
333
+ elif data and var_data:
334
+ raise ValueError("Both data and var_data cannot be provided simultaneously")
335
+ else:
336
+ result = function()
337
+
338
+ return result
339
+ except error_value as e:
340
+ if error:
341
+ raise e
342
+ elif error_msg:
343
+ print_error_msg(error_msg, f': {e}')
344
+ return False
345
+ def all_try_json_loads(data, error=False, error_msg=None, error_value=(json.JSONDecodeError, TypeError)):
346
+ return all_try(data=data, function=json.loads, error=error, error_msg=error_msg, error_value=error_value)
347
+
348
+ def safe_json_loadss(data, default_value=None, error=False, error_msg=None):
349
+ """ Safely attempts to load a JSON string. Returns the original data or a default value if parsing fails.
350
+ Args:
351
+ data (str): The JSON string to parse.
352
+ default_value (any, optional): The value to return if parsing fails. Defaults to None.
353
+ error (bool, optional): Whether to raise an error if parsing fails. Defaults to False.
354
+ error_msg (str, optional): The error message to display if parsing fails. Defaults to None.
355
+
356
+ Returns:
357
+ any: The parsed JSON object, or the original data/default value if parsing fails.
358
+ """
359
+ if isinstance(data,dict):
360
+ return data
361
+ try_json = all_try_json_loads(data=data, error=error, error_msg=error_msg)
362
+ if try_json:
363
+ return try_json
364
+ if default_value:
365
+ data = default_value
366
+ return data
367
+ def clean_invalid_newlines(json_string: str,line_replacement_value='') -> str:
368
+ """ Removes invalid newlines from a JSON string that are not within double quotes.
369
+ Args:
370
+ json_string (str): The JSON string containing newlines.
371
+
372
+ Returns:
373
+ str: The JSON string with invalid newlines removed.
374
+ """
375
+ pattern = r'(?<!\\)\n(?!([^"]*"[^"]*")*[^"]*$)'
376
+ return re.sub(pattern, line_replacement_value, json_string)
377
+ def get_value_from_path(json_data, path,line_replacement_value='*n*'):
378
+ """ Traverses a nested JSON object using a specified path and returns the value at the end of that path.
379
+ Args:
380
+ json_data (dict/list): The JSON object to traverse.
381
+ path (list): The path to follow in the JSON object.
382
+
383
+ Returns:
384
+ any: The value at the end of the specified path.
385
+ """
386
+ current_data = safe_json_loads(json_data)
387
+ for step in path:
388
+ current_data = safe_json_loads(current_data[step])
389
+ if isinstance(current_data, str):
390
+ current_data = read_malformed_json(current_data,line_replacement_value=line_replacement_value)
391
+ return current_data
392
+ def find_paths_to_key(json_data, key_to_find,line_replacement_value='*n*'):
393
+ """ Searches a nested JSON object for all paths that lead to a specified key.
394
+ Args:
395
+ json_data (dict/list): The JSON object to search.
396
+ key_to_find (str): The key to search for in the JSON object.
397
+
398
+ Returns:
399
+ list: A list of paths (each path is a list of keys/indices) leading to the specified key.
400
+ """
401
+ def _search_path(data, current_path):
402
+ if isinstance(data, dict):
403
+ for key, value in data.items():
404
+ new_path = current_path + [key]
405
+ if key == key_to_find:
406
+ paths.append(new_path)
407
+ if isinstance(value, str):
408
+ try:
409
+ json_data = read_malformed_json(value,line_replacement_value=line_replacement_value)
410
+ _search_path(json_data, new_path)
411
+ except json.JSONDecodeError:
412
+ pass
413
+ _search_path(value, new_path)
414
+ elif isinstance(data, list):
415
+ for index, item in enumerate(data):
416
+ new_path = current_path + [index]
417
+ _search_path(item, new_path)
418
+
419
+ paths = []
420
+ _search_path(json_data, [])
421
+ return paths
422
+ def read_malformed_json(json_string,line_replacement_value="*n"):
423
+ """ Attempts to parse a malformed JSON string after cleaning it.
424
+ Args:
425
+ json_string (str): The malformed JSON string.
426
+
427
+ Returns:
428
+ any: The parsed JSON object.
429
+ """
430
+ if isinstance(json_string, str):
431
+ json_string = clean_invalid_newlines(json_string,line_replacement_value=line_replacement_value)
432
+ return safe_json_loads(json_string)
433
+ def get_any_value(json_obj, key,line_replacement_value="*n*"):
434
+ """ Fetches the value associated with a specified key from a JSON object or file. If the provided input is a file path, it reads the file first.
435
+ Args:
436
+ json_obj (dict/list/str): The JSON object or file path containing the JSON object.
437
+ key (str): The key to search for in the JSON object.
438
+
439
+ Returns:
440
+ any: The value associated with the specified key.
441
+ """
442
+ if isinstance(json_obj,str):
443
+ if os.path.isfile(json_obj):
444
+ with open(json_obj, 'r', encoding='UTF-8') as f:
445
+ json_obj=f.read()
446
+ json_data = read_malformed_json(json_obj)
447
+ paths_to_value = find_paths_to_key(json_data, key)
448
+ if not isinstance(paths_to_value, list):
449
+ paths_to_value = [paths_to_value]
450
+ for i, path_to_value in enumerate(paths_to_value):
451
+ paths_to_value[i] = get_value_from_path(json_data, path_to_value)
452
+ if isinstance(paths_to_value[i],str):
453
+ paths_to_value[i]=paths_to_value[i].replace(line_replacement_value,'\n')
454
+ if isinstance(paths_to_value,list):
455
+ if len(paths_to_value) == 0:
456
+ paths_to_value=None
457
+ elif len(paths_to_value)==1:
458
+ paths_to_value = paths_to_value[0]
459
+ return paths_to_value
460
+ def format_json_key_values(json_data, indent=0):
461
+ formatted_string = ""
462
+
463
+ # Check if the input is a string and try to parse it as JSON
464
+ if isinstance(json_data, str):
465
+ try:
466
+ json_data = json.loads(json_data)
467
+ except json.JSONDecodeError:
468
+ return "Invalid JSON string"
469
+
470
+ # Function to format individual items based on their type
471
+ def format_item(item, indent):
472
+ if isinstance(item, dict):
473
+ return format_json_key_values(item, indent)
474
+ elif isinstance(item, list):
475
+ return format_list(item, indent)
476
+ else:
477
+ return ' ' * indent + str(item) + "\n"
478
+
479
+ # Function to format lists
480
+ def format_list(lst, indent):
481
+ lst_str = ""
482
+ for elem in lst:
483
+ lst_str += format_item(elem, indent + 1)
484
+ return lst_str
485
+
486
+ # Iterate over each key-value pair
487
+ for key, value in json_data.items():
488
+ # Append the key with appropriate indentation
489
+ formatted_string += ' ' * indent + f"{key}:\n"
490
+
491
+ # Recursively format the value based on its type
492
+ formatted_string += format_item(value, indent)
493
+
494
+ return formatted_string
495
+
496
+ def find_matching_dicts(dict_objs:(dict or list)=None,keys:(str or list)=None,values:(str or list)=None):
497
+ values = make_list(values) if values is not None else []
498
+ dict_objs = make_list(dict_objs) if dict_objs is not None else [{}]
499
+ keys = make_list(keys) if keys is not None else []
500
+ bool_list_og = [False for i in range(len(keys))]
501
+ found_dicts = []
502
+ for dict_obj in dict_objs:
503
+ bool_list = bool_list_og
504
+ for i,key in enumerate(keys):
505
+ if key in list(dict_obj.keys()):
506
+ if dict_obj[key] == values[i]:
507
+ bool_list[i]=True
508
+ if False not in bool_list:
509
+ found_dicts.append(dict_obj)
510
+ return found_dicts
511
+
512
+ def closest_dictionary(dict_objs:dict=None,values:(str or list)=None):
513
+ values = make_list(values) if values is not None else []
514
+ dict_objs = make_list(dict_objs) if dict_objs is not None else [{}]
515
+ total_values = [value for dict_obj in dict_objs for value in dict_obj.values()]
516
+ matched_objs = [get_closest_match_from_list(value, total_values) for value in values]
517
+ bool_list_og = [False for i in range(len(matched_objs))]
518
+ for dict_obj in dict_objs:
519
+ bool_list = bool_list_og
520
+ for key, key_value in dict_obj.items():
521
+ for i,matched_obj in enumerate(matched_objs):
522
+ if key_value.lower() == matched_obj.lower():
523
+ bool_list[i]=True
524
+ if False not in bool_list:
525
+ return dict_obj
526
+ return None
527
+
528
+ def get_dict_from_string(string, file_path=None):
529
+ bracket_count = 0
530
+ start_index = None
531
+ for i, char in enumerate(string):
532
+ if char == '{':
533
+ bracket_count += 1
534
+ if start_index is None:
535
+ start_index = i
536
+ elif char == '}':
537
+ bracket_count -= 1
538
+ if bracket_count == 0 and start_index is not None:
539
+ json_data = safe_json_loads(string[start_index:i+1])
540
+ if file_path:
541
+ safe_dump_to_file(file_path=mkdirs(file_path), data=json_data)
542
+ return json_data
543
+ return None
544
+
545
+ def closest_dictionary(dict_objs=None, values=None):
546
+ values = make_list(values) if values is not None else []
547
+ dict_objs = make_list(dict_objs) if dict_objs is not None else [{}]
548
+ total_values = [value for dict_obj in dict_objs for value in dict_obj.values()]
549
+ matched_objs = [get_closest_match_from_list(value, total_values) for value in values]
550
+
551
+ for dict_obj in dict_objs:
552
+ # Using all() with a generator expression for efficiency
553
+ if all(match in convert_and_normalize_values(dict_obj.values()) for match in matched_objs):
554
+ return dict_obj
555
+ return None
556
+
557
+ def get_all_keys(dict_data,keys=[]):
558
+ if isinstance(dict_data,dict):
559
+ for key,value in dict_data.items():
560
+ keys.append(key)
561
+ keys = get_all_keys(value,keys=keys)
562
+ return keys
563
+
564
+ def update_dict_value(data, paths, new_value):
565
+ """
566
+ Traverses a dictionary to the specified key path and updates its value.
567
+
568
+ Args:
569
+ data (dict): The dictionary to traverse.
570
+ paths (list): The list of keys leading to the target value.
571
+ new_value (any): The new value to assign to the specified key.
572
+
573
+ Returns:
574
+ dict: The updated dictionary.
575
+ """
576
+ d = data
577
+ for key in paths[:-1]:
578
+ # Traverse the dictionary up to the second-to-last key
579
+ d = d[key]
580
+ # Update the value at the final key
581
+ d[paths[-1]] = new_value
582
+ return data
583
+ def get_all_key_values(keys=None,dict_obj=None):
584
+ keys = keys or []
585
+ dict_obj = dict_obj or {}
586
+ new_dict_obj = {}
587
+ for key in keys:
588
+ values = dict_obj.get(key)
589
+ if values:
590
+ new_dict_obj[key]=values
591
+ return new_dict_obj
592
+
593
+ def get_all_values(keys=None,dict_obj=None):
594
+ keys = keys or []
595
+ dict_obj = dict_obj or {}
596
+ values=[]
597
+ for key in keys:
598
+ value = dict_obj.get(key)
599
+ if value:
600
+ values.append(value)
601
+ return values
602
+
603
+ def safe_update_json_datas(
604
+ json_data: dict,
605
+ update_data: dict,
606
+ valid_keys: list[str] | None = None,
607
+ invalid_keys: list[str] | None = None
608
+ ) -> dict:
609
+ """
610
+ - If valid_keys is provided (non-empty), only update keys in that list.
611
+ - Else if invalid_keys is provided, update all keys except those in invalid_keys,
612
+ and delete any existing keys that are in invalid_keys.
613
+ - Else update every key.
614
+ In all cases, overwrite values unconditionally.
615
+ """
616
+ valid = set(make_list(valid_keys or []))
617
+ invalid = set(make_list(invalid_keys or []))
618
+
619
+ for key, value in update_data.items():
620
+ if valid:
621
+ if key in valid:
622
+ json_data[key] = value
623
+ elif invalid:
624
+ if key in invalid:
625
+ json_data.pop(key, None)
626
+ else:
627
+ json_data[key] = value
628
+ else:
629
+ json_data[key] = value
630
+
631
+ return json_data
632
+
633
+ def get_json_file_path(file_path,data=None):
634
+ data = data or {}
635
+ if not os.path.isfile(file_path):
636
+ safe_dump_to_file(data={},file_path=file_path)
637
+ return file_path
638
+
639
+ def get_json_file_data(file_path):
640
+ if os.path.isfile(file_path):
641
+ return safe_load_from_json(file_path)
642
+
643
+ def get_create_json_data(file_path,data=None):
644
+ get_json_file_path(file_path,data=data)
645
+ return get_json_file_data(file_path)
646
+
647
+ def get_json_data(file_path):
648
+ file_path = get_file_path(file_path)
649
+ data = safe_read_from_json(file_path)
650
+ return data
651
+
652
+ def save_updated_json_data(data,file_path):
653
+ data = data or {}
654
+ new_data = get_json_data(file_path)
655
+ new_data.update(data)
656
+ safe_dump_to_file(new_data,file_path)
657
+
658
+ def safe_updated_json_data(
659
+ data,
660
+ file_path,
661
+ valid_keys=None,
662
+ invalid_keys=None
663
+ ):
664
+ update_data = data or {}
665
+ json_data = get_create_json_data(file_path, data={})
666
+ new_data = safe_update_json_datas(
667
+ json_data=json_data,
668
+ update_data=update_data,
669
+ valid_keys=valid_keys,
670
+ invalid_keys=invalid_keys # ← now correct
671
+ )
672
+ return new_data
673
+ def safe_save_updated_json_data(data,
674
+ file_path,
675
+ valid_keys=None,
676
+ invalid_keys=None
677
+ ):
678
+ new_data = safe_updated_json_data(data=data,
679
+ file_path=file_path,
680
+ valid_keys=valid_keys,
681
+ invalid_keys=invalid_keys
682
+ )
683
+ safe_dump_to_file(new_data,file_path)
684
+ return new_data
685
+
686
+ def get_result_from_data(key,func,**data):
687
+ result_data = func(**data)
688
+ result = result_data.get(key)
689
+ return result
690
+
691
+ def dump_if_json(obj):
692
+ """Convert a dictionary to a JSON string if the object is a dictionary."""
693
+ if isinstance(obj, dict):
694
+ return json.dumps(obj)
695
+ return obj
696
+ def get_desired_key_values(obj,keys=None,defaults=None):
697
+ defaults = defaults or {}
698
+ if keys == None:
699
+ return obj
700
+ new_dict={}
701
+ for key,value in defaults.items():
702
+ new_dict[key] = obj.get(key) or defaults.get(key)
703
+ if obj and isinstance(obj,dict):
704
+ for key in keys:
705
+ new_dict[key] = obj.get(key) or defaults.get(key)
706
+ return new_dict
707
+ def makeParams(*arg,**kwargs):
708
+ arg=make_list(arg)
709
+ arg.append({k: v for k, v in kwargs.items() if v is not None})
710
+ return arg
711
+
712
+ def get_only_kwargs(varList,*args,**kwargs):
713
+ new_kwargs={}
714
+ for i,arg in enumerate(args):
715
+ key_variable = varList[i]
716
+ kwargs[key_variable]=arg
717
+ for key,value in kwargs.items():
718
+ if key in varList:
719
+ new_kwargs[key] = value
720
+ return new_kwargs
721
+
722
+ def flatten_json(data, parent_key='', sep='_'):
723
+ """
724
+ Flatten a JSON object into a single dictionary with keys indicating the nested structure.
725
+
726
+ Args:
727
+ data (dict): The JSON object to flatten.
728
+ parent_key (str): The base key to use for nested keys (used in recursive calls).
729
+ sep (str): The separator to use between keys.
730
+
731
+ Returns:
732
+ dict: The flattened JSON object.
733
+ """
734
+ items = []
735
+ if isinstance(data, dict):
736
+ for key, value in data.items():
737
+ new_key = f"{parent_key}{sep}{key}" if parent_key else key
738
+ if isinstance(value, dict):
739
+ items.extend(flatten_json(value, new_key, sep=sep).items())
740
+ elif isinstance(value, list):
741
+ for i, item in enumerate(value):
742
+ items.extend(flatten_json(item, f"{new_key}{sep}{i}", sep=sep).items())
743
+ else:
744
+ items.append((new_key, value))
745
+ elif isinstance(data, list):
746
+ for i, item in enumerate(data):
747
+ items.extend(flatten_json(item, f"{parent_key}{sep}{i}", sep=sep).items())
748
+ else:
749
+ items.append((parent_key, data))
750
+
751
+ return dict(items)
752
+
753
+
754
+ def to_json_safe(obj):
755
+ if obj is None:
756
+ return None
757
+
758
+ if isinstance(obj, (str, int, float, bool)):
759
+ return obj
760
+
761
+ if isinstance(obj, (datetime, date)):
762
+ return obj.isoformat()
763
+
764
+ if isinstance(obj, Decimal):
765
+ return float(obj)
766
+
767
+ if isinstance(obj, Path):
768
+ return str(obj)
769
+
770
+ if isinstance(obj, dict):
771
+ return {k: to_json_safe(v) for k, v in obj.items()}
772
+
773
+ if isinstance(obj, (list, tuple, set)):
774
+ return [to_json_safe(v) for v in obj]
775
+
776
+ # Fallback (yt-dlp objects, enums, etc.)
777
+ return str(obj)