lionagi 0.0.115__py3-none-any.whl → 0.0.204__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. lionagi/__init__.py +1 -2
  2. lionagi/_services/__init__.py +5 -0
  3. lionagi/_services/anthropic.py +79 -0
  4. lionagi/_services/base_service.py +414 -0
  5. lionagi/_services/oai.py +98 -0
  6. lionagi/_services/openrouter.py +44 -0
  7. lionagi/_services/services.py +91 -0
  8. lionagi/_services/transformers.py +46 -0
  9. lionagi/bridge/langchain.py +26 -16
  10. lionagi/bridge/llama_index.py +50 -20
  11. lionagi/configs/oai_configs.py +2 -14
  12. lionagi/configs/openrouter_configs.py +2 -2
  13. lionagi/core/__init__.py +7 -8
  14. lionagi/core/branch/branch.py +589 -0
  15. lionagi/core/branch/branch_manager.py +139 -0
  16. lionagi/core/branch/conversation.py +484 -0
  17. lionagi/core/core_util.py +59 -0
  18. lionagi/core/flow/flow.py +19 -0
  19. lionagi/core/flow/flow_util.py +62 -0
  20. lionagi/core/instruction_set/__init__.py +0 -5
  21. lionagi/core/instruction_set/instruction_set.py +343 -0
  22. lionagi/core/messages/messages.py +176 -0
  23. lionagi/core/sessions/__init__.py +0 -5
  24. lionagi/core/sessions/session.py +428 -0
  25. lionagi/loaders/chunker.py +51 -47
  26. lionagi/loaders/load_util.py +2 -2
  27. lionagi/loaders/reader.py +45 -39
  28. lionagi/models/imodel.py +53 -0
  29. lionagi/schema/async_queue.py +158 -0
  30. lionagi/schema/base_node.py +318 -147
  31. lionagi/schema/base_tool.py +31 -1
  32. lionagi/schema/data_logger.py +74 -38
  33. lionagi/schema/data_node.py +57 -6
  34. lionagi/structures/graph.py +132 -10
  35. lionagi/structures/relationship.py +58 -20
  36. lionagi/structures/structure.py +36 -25
  37. lionagi/tests/test_utils/test_api_util.py +219 -0
  38. lionagi/tests/test_utils/test_call_util.py +785 -0
  39. lionagi/tests/test_utils/test_encrypt_util.py +323 -0
  40. lionagi/tests/test_utils/test_io_util.py +238 -0
  41. lionagi/tests/test_utils/test_nested_util.py +338 -0
  42. lionagi/tests/test_utils/test_sys_util.py +358 -0
  43. lionagi/tools/tool_manager.py +186 -0
  44. lionagi/tools/tool_util.py +266 -3
  45. lionagi/utils/__init__.py +21 -61
  46. lionagi/utils/api_util.py +359 -71
  47. lionagi/utils/call_util.py +839 -264
  48. lionagi/utils/encrypt_util.py +283 -16
  49. lionagi/utils/io_util.py +178 -93
  50. lionagi/utils/nested_util.py +672 -0
  51. lionagi/utils/pd_util.py +57 -0
  52. lionagi/utils/sys_util.py +284 -156
  53. lionagi/utils/url_util.py +55 -0
  54. lionagi/version.py +1 -1
  55. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/METADATA +21 -17
  56. lionagi-0.0.204.dist-info/RECORD +106 -0
  57. lionagi/core/conversations/__init__.py +0 -5
  58. lionagi/core/conversations/conversation.py +0 -107
  59. lionagi/core/flows/__init__.py +0 -8
  60. lionagi/core/flows/flow.py +0 -8
  61. lionagi/core/flows/flow_util.py +0 -62
  62. lionagi/core/instruction_set/instruction_sets.py +0 -7
  63. lionagi/core/sessions/sessions.py +0 -185
  64. lionagi/endpoints/__init__.py +0 -5
  65. lionagi/endpoints/audio.py +0 -17
  66. lionagi/endpoints/chatcompletion.py +0 -54
  67. lionagi/messages/__init__.py +0 -11
  68. lionagi/messages/instruction.py +0 -15
  69. lionagi/messages/message.py +0 -110
  70. lionagi/messages/response.py +0 -33
  71. lionagi/messages/system.py +0 -12
  72. lionagi/objs/__init__.py +0 -11
  73. lionagi/objs/abc_objs.py +0 -39
  74. lionagi/objs/async_queue.py +0 -135
  75. lionagi/objs/messenger.py +0 -85
  76. lionagi/objs/tool_manager.py +0 -253
  77. lionagi/services/__init__.py +0 -11
  78. lionagi/services/base_api_service.py +0 -230
  79. lionagi/services/oai.py +0 -34
  80. lionagi/services/openrouter.py +0 -31
  81. lionagi/tests/test_api_util.py +0 -46
  82. lionagi/tests/test_call_util.py +0 -115
  83. lionagi/tests/test_convert_util.py +0 -202
  84. lionagi/tests/test_encrypt_util.py +0 -33
  85. lionagi/tests/test_flat_util.py +0 -426
  86. lionagi/tests/test_sys_util.py +0 -0
  87. lionagi/utils/convert_util.py +0 -229
  88. lionagi/utils/flat_util.py +0 -599
  89. lionagi-0.0.115.dist-info/RECORD +0 -110
  90. /lionagi/{services → _services}/anyscale.py +0 -0
  91. /lionagi/{services → _services}/azure.py +0 -0
  92. /lionagi/{services → _services}/bedrock.py +0 -0
  93. /lionagi/{services → _services}/everlyai.py +0 -0
  94. /lionagi/{services → _services}/gemini.py +0 -0
  95. /lionagi/{services → _services}/gpt4all.py +0 -0
  96. /lionagi/{services → _services}/huggingface.py +0 -0
  97. /lionagi/{services → _services}/litellm.py +0 -0
  98. /lionagi/{services → _services}/localai.py +0 -0
  99. /lionagi/{services → _services}/mistralai.py +0 -0
  100. /lionagi/{services → _services}/ollama.py +0 -0
  101. /lionagi/{services → _services}/openllm.py +0 -0
  102. /lionagi/{services → _services}/perplexity.py +0 -0
  103. /lionagi/{services → _services}/predibase.py +0 -0
  104. /lionagi/{services → _services}/rungpt.py +0 -0
  105. /lionagi/{services → _services}/vllm.py +0 -0
  106. /lionagi/{services → _services}/xinference.py +0 -0
  107. /lionagi/{endpoints/assistants.py → agents/__init__.py} +0 -0
  108. /lionagi/{tools → agents}/planner.py +0 -0
  109. /lionagi/{tools → agents}/prompter.py +0 -0
  110. /lionagi/{tools → agents}/scorer.py +0 -0
  111. /lionagi/{tools → agents}/summarizer.py +0 -0
  112. /lionagi/{tools → agents}/validator.py +0 -0
  113. /lionagi/{endpoints/embeddings.py → core/branch/__init__.py} +0 -0
  114. /lionagi/{services/anthropic.py → core/branch/cluster.py} +0 -0
  115. /lionagi/{endpoints/finetune.py → core/flow/__init__.py} +0 -0
  116. /lionagi/{endpoints/image.py → core/messages/__init__.py} +0 -0
  117. /lionagi/{endpoints/moderation.py → models/__init__.py} +0 -0
  118. /lionagi/{endpoints/vision.py → models/base_model.py} +0 -0
  119. /lionagi/{objs → schema}/status_tracker.py +0 -0
  120. /lionagi/tests/{test_io_util.py → test_utils/__init__.py} +0 -0
  121. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
  122. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
  123. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,283 @@
1
- # this module has no internal dependency
2
- from cryptography.fernet import Fernet
3
-
4
- def generate_encryption_key() -> str:
5
- """Generates a key for encryption."""
6
- return Fernet.generate_key().decode()
7
-
8
- def encrypt(data: str, key: str) -> str:
9
- """Encrypts data using the provided key."""
10
- fernet = Fernet(key.encode())
11
- return fernet.encrypt(data.encode()).decode()
12
-
13
- def decrypt(data: str, key: str) -> str:
14
- """Decrypts data using the provided key."""
15
- fernet = Fernet(key.encode())
16
- return fernet.decrypt(data.encode()).decode()
1
+ import base64
2
+ import binascii
3
+ import hashlib
4
+ import os
5
+ import zipfile
6
+ from base64 import urlsafe_b64encode
7
+ from cryptography.fernet import Fernet, InvalidToken
8
+ from cryptography.hazmat.backends import default_backend
9
+ from cryptography.hazmat.primitives import hashes
10
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
11
+ from typing import Optional
12
+
13
+
14
+ class EncrytionUtil:
15
+ """
16
+ A utility class for handling encryption, decryption, file operations, and password strength checking.
17
+ """
18
+
19
+ @staticmethod
20
+ def password_strength_checker(password: str) -> bool:
21
+ """
22
+ Check the strength of a password.
23
+
24
+ Args:
25
+ password (str): The password to check.
26
+
27
+ Returns:
28
+ bool: True if the password is strong, False otherwise.
29
+
30
+ Examples:
31
+ >>> password_strength_checker("Weakpass")
32
+ False
33
+ >>> password_strength_checker("Strongpass1")
34
+ True
35
+ """
36
+ if len(password) < 8 or not any(char.isdigit() for char in password) or not any(char.isupper() for char in password):
37
+ return False
38
+ return True
39
+
40
+ @staticmethod
41
+ def generate_encryption_key(password: Optional[str] = None, salt: Optional[bytes] = None) -> str:
42
+ """
43
+ Generate an encryption key from a password and salt.
44
+
45
+ Args:
46
+ password (Optional[str]): The password to derive the key from. If None, a random key is generated.
47
+ salt (Optional[bytes]): A salt for the key derivation. If None, a random salt is used.
48
+
49
+ Returns:
50
+ str: The generated encryption key as a URL-safe base64-encoded string.
51
+
52
+ Raises:
53
+ ValueError: If the password is too weak.
54
+
55
+ Examples:
56
+ >>> key = generate_encryption_key("Strongpass1")
57
+ >>> isinstance(key, str)
58
+ True
59
+ """
60
+ if password:
61
+ if not EncrytionUtil.password_strength_checker(password):
62
+ raise ValueError("Password is too weak.")
63
+ if not salt:
64
+ salt = os.urandom(16)
65
+ kdf = PBKDF2HMAC(
66
+ algorithm=hashes.SHA256(),
67
+ length=32,
68
+ salt=salt,
69
+ iterations=100000,
70
+ backend=default_backend()
71
+ )
72
+ key = kdf.derive(password.encode())
73
+ return urlsafe_b64encode(key).decode()
74
+ else:
75
+ return Fernet.generate_key().decode()
76
+
77
+ @staticmethod
78
+ def encrypt(data: str, key: str) -> str:
79
+ """
80
+ Encrypt the provided data using the provided key.
81
+
82
+ Args:
83
+ data (str): The data to encrypt.
84
+ key (str): The encryption key.
85
+
86
+ Returns:
87
+ str: The encrypted data.
88
+
89
+ Example:
90
+ >>> encrypt("This is some test data.", "esvCExTuhddRb8kSobU_gnNRYObyTRTI2LJF4nYai5I=")
91
+ 'gAAAAABloX9m_S_G1VUtbfGUDB7ooHJNt8sPiSCwnp1ehe6dExvMEqtw_ua2ELk_uUbLoB6a1XkbKLOkM4UBvwmk6sMoY-yNvE-Lv-w-VNnfzf89zH82rgI='
92
+ """
93
+ fernet = Fernet(key.encode())
94
+ return fernet.encrypt(data.encode()).decode()
95
+
96
+ @staticmethod
97
+ def decrypt(data: str, key: str) -> str:
98
+ """
99
+ Decrypt the provided data using the provided key.
100
+
101
+ Args:
102
+ data (str): The data to decrypt.
103
+ key (str): The encryption key.
104
+
105
+ Returns:
106
+ str: The decrypted data.
107
+
108
+ Example:
109
+ >>> decrypt('gAAAAABloX9m_S_G1VUtbfGUDB7ooHJNt8sPiSCwnp1ehe6dExvMEqtw_ua2ELk_uUbLoB6a1XkbKLOkM4UBvwmk6sMoY-yNvE-Lv-w-VNnfzf89zH82rgI=', "esvCExTuhddRb8kSobU_gnNRYObyTRTI2LJF4nYai5I=")
110
+ 'This is some test data.'
111
+ """
112
+ fernet = Fernet(key.encode())
113
+ return fernet.decrypt(data.encode()).decode()
114
+
115
+ @staticmethod
116
+ def encrypt_file(file_path: str, key: str, output_path: str = None):
117
+ """
118
+ Encrypt a file.
119
+
120
+ Args:
121
+ file_path (str): The path of the file to encrypt.
122
+ key (str): The encryption key.
123
+ output_path (str, optional): The path to save the encrypted file. If not provided, '.enc' is appended to the input file path.
124
+
125
+ Example:
126
+ >>> encrypt_file("test_file.txt", "esvCExTuhddRb8kSobU_gnNRYObyTRTI2LJF4nYai5I=")
127
+ """
128
+ if not output_path:
129
+ output_path = file_path + '.enc'
130
+ with open(file_path, 'rb') as file_to_encrypt:
131
+ encrypted_data = EncrytionUtil.encrypt(file_to_encrypt.read().decode(), key)
132
+ with open(output_path, 'wb') as encrypted_file:
133
+ encrypted_file.write(encrypted_data.encode())
134
+
135
+ @staticmethod
136
+ def decrypt_file(encrypted_file_path: str, key: str, output_path: str = None):
137
+ """
138
+ Decrypt a file.
139
+
140
+ Args:
141
+ encrypted_file_path (str): The path of the file to decrypt.
142
+ key (str): The encryption key.
143
+ output_path (str, optional): The path to save the decrypted file. If not provided, '.enc' is removed from the input file path.
144
+
145
+ Example:
146
+ >>> decrypt_file("test_file.txt.enc", "esvCExTuhddRb8kSobU_gnNRYObyTRTI2LJF4nYai5I=")
147
+ """
148
+ if not output_path:
149
+ output_path = encrypted_file_path.replace('.enc', '')
150
+ with open(encrypted_file_path, 'rb') as encrypted_file:
151
+ decrypted_data = EncrytionUtil.decrypt(encrypted_file.read().decode(), key)
152
+ with open(output_path, 'wb') as decrypted_file:
153
+ decrypted_file.write(decrypted_data.encode())
154
+
155
+ @staticmethod
156
+ def is_encrypted(file_path: str, key: str) -> bool:
157
+ """
158
+ Check if a file is encrypted.
159
+
160
+ Args:
161
+ file_path (str): The path of the file to check.
162
+ key (str): The encryption key.
163
+
164
+ Returns:
165
+ bool: True if the file is encrypted, False otherwise.
166
+
167
+ Example:
168
+ >>> is_encrypted("test_file.txt.enc", "esvCExTuhddRb8kSobU_gnNRYObyTRTI2LJF4nYai5I=")
169
+ True
170
+ """
171
+ try:
172
+ EncrytionUtil.decrypt_file(file_path, key, "temp_decrypted_file")
173
+ os.remove("temp_decrypted_file")
174
+ return True
175
+ except InvalidToken:
176
+ return False
177
+
178
+ @staticmethod
179
+ def decompress_file(file_path: str, output_path: str = None):
180
+ """
181
+ Decompress a file.
182
+
183
+ Args:
184
+ file_path (str): The path of the file to decompress.
185
+ output_path (str, optional): The path to save the decompressed file. If not provided, the file is decompressed in the same directory.
186
+
187
+ Example:
188
+ >>> decompress_file("test_file.txt.zip")
189
+ """
190
+ if not output_path:
191
+ output_path = os.path.dirname(file_path)
192
+ with zipfile.ZipFile(file_path, 'r') as zipf:
193
+ zipf.extractall(output_path)
194
+
195
+ @staticmethod
196
+ def compress_file(file_path: str, output_path: str = None):
197
+ """
198
+ Compress a file.
199
+
200
+ Args:
201
+ file_path (str): The path of the file to compress.
202
+ output_path (str, optional): The path to save the compressed file. If not provided, '.zip' is appended to the input file path.
203
+
204
+ Example:
205
+ >>> compress_file("test_file.txt")
206
+ """
207
+ if not output_path:
208
+ output_path = file_path + '.zip'
209
+ with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
210
+ zipf.write(file_path)
211
+
212
+ @staticmethod
213
+ def binary_to_hex(data: bytes) -> str:
214
+ """
215
+ Convert binary data to a hexadecimal string representation.
216
+
217
+ Args:
218
+ data: A bytes object containing binary data.
219
+
220
+ Returns:
221
+ A string containing the hexadecimal representation of the binary data.
222
+
223
+ Examples:
224
+ >>> binary_to_hex(b'\x00\x0F')
225
+ '000f'
226
+ >>> binary_to_hex(b'hello')
227
+ '68656c6c6f'
228
+ """
229
+ return binascii.hexlify(data).decode()
230
+
231
+ @staticmethod
232
+ def create_hash(data: str, algorithm: str = 'sha256') -> str:
233
+ """
234
+ Create a hash of the given data using the specified algorithm.
235
+
236
+ Args:
237
+ data: The string to hash.
238
+ algorithm: The hashing algorithm to use (default is 'sha256').
239
+
240
+ Returns:
241
+ The hexadecimal digest of the hash.
242
+
243
+ Examples:
244
+ >>> create_hash('hello')
245
+ '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
246
+ """
247
+ hasher = hashlib.new(algorithm)
248
+ hasher.update(data.encode())
249
+ return hasher.hexdigest()
250
+
251
+ @staticmethod
252
+ def decode_base64(data: str) -> str:
253
+ """
254
+ Decode a base64 encoded string.
255
+
256
+ Args:
257
+ data: A base64 encoded string.
258
+
259
+ Returns:
260
+ A decoded string.
261
+
262
+ Examples:
263
+ >>> decode_base64('SGVsbG8sIFdvcmxkIQ==')
264
+ 'Hello, World!'
265
+ """
266
+ return base64.b64decode(data).decode()
267
+
268
+ @staticmethod
269
+ def encode_base64(data: str) -> str:
270
+ """
271
+ Encode a string using base64 encoding.
272
+
273
+ Args:
274
+ data: A string to be encoded.
275
+
276
+ Returns:
277
+ A base64 encoded string.
278
+
279
+ Examples:
280
+ >>> encode_base64("Hello, World!")
281
+ 'SGVsbG8sIFdvcmxkIQ=='
282
+ """
283
+ return base64.b64encode(data.encode()).decode()
lionagi/utils/io_util.py CHANGED
@@ -1,102 +1,187 @@
1
- # used flat_util
2
1
  import csv
3
2
  import json
4
3
  import os
5
4
  import tempfile
5
+ from collections.abc import Iterable
6
6
  from typing import Any, Dict, List
7
- from .flat_util import to_list
8
7
 
9
-
10
- def to_temp(input: Any,
11
- flatten_dict: bool = False,
12
- flat: bool = False,
13
- dropna: bool = False):
8
+ class IOUtil:
14
9
  """
15
- Converts input to a list and writes it to a temporary file in JSON format, with flattening options.
16
-
17
- This function serializes data to a temporary JSON file, useful for transient storage or testing.
18
- It includes options to flatten the input if it contains dictionaries or lists.
19
-
20
- Parameters:
21
- input (Any): The data to be converted and written to a file.
22
-
23
- flatten_dict (bool, optional): Flatten dictionaries in the input. Defaults to False.
24
-
25
- flat (bool, optional): Flatten lists in the input. Defaults to False.
26
-
27
- dropna (bool, optional): Exclude 'None' values during flattening. Defaults to False.
28
-
29
- Raises:
30
- TypeError: If the input is not JSON serializable.
31
-
32
- Example:
33
- >>> temp_file = to_temp({'a': 1, 'b': [2, 3]}, flatten_dict=True)
34
- >>> temp_file.name # Doctest: +ELLIPSIS
35
- '/var/folders/.../tmp...'
10
+ Utility class for reading and writing data from/to files in various formats.
36
11
  """
37
- input = to_list(input, flatten_dict, flat, dropna)
38
12
 
39
- temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
40
- try:
41
- json.dump(input, temp_file)
42
- except TypeError as e:
43
- temp_file.close() # Ensuring file closure before raising error
44
- raise TypeError(f"Data provided is not JSON serializable: {e}")
45
- temp_file.close()
46
- return temp_file
47
-
48
- def to_csv(input: List[Dict[str, Any]]=None,
49
- filepath: str=None,
50
- file_exist_ok: bool = False) -> None:
51
- """
52
- Writes a list of dictionaries to a CSV file, with dictionary keys as headers.
53
-
54
- This function writes a list of dictionaries to a CSV file. It checks if the file exists
55
- and handles file creation based on the 'file_exist_ok' flag.
56
-
57
- Parameters:
58
- input (List[Dict[str, Any]]): Data to write to the CSV file.
59
-
60
- filepath (str): Path of the output CSV file.
61
-
62
- file_exist_ok (bool, optional): Create the file if it doesn't exist. Defaults to False.
63
-
64
- Raises:
65
- FileExistsError: If the file already exists and 'file_exist_ok' is False.
66
-
67
- Example:
68
- >>> data = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
69
- >>> to_csv(data, 'people.csv')
70
- """
71
-
72
- if not os.path.exists(os.path.dirname(filepath)) and os.path.dirname(filepath) != '':
73
- if file_exist_ok:
74
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
75
- else:
76
- raise FileNotFoundError(f"The directory {os.path.dirname(filepath)} does not exist.")
77
-
78
- with open(filepath, 'w', newline='') as csv_file:
79
- writer = csv.DictWriter(csv_file, fieldnames=input[0].keys())
80
- writer.writeheader()
81
- writer.writerows(input)
82
-
83
- def append_to_jsonl(data: Any, filepath: str) -> None:
84
- """
85
- Appends data to a JSON lines (jsonl) file.
86
-
87
- Serializes given data to a JSON-formatted string and appends it to a jsonl file.
88
- Useful for logging or data collection where entries are added incrementally.
89
-
90
- Parameters:
91
- data (Any): Data to be serialized and appended.
92
-
93
- filepath (str): Path to the jsonl file.
94
-
95
- Example:
96
- >>> append_to_jsonl({"key": "value"}, "data.jsonl")
97
- # Appends {"key": "value"} to 'data.jsonl'
98
- """
99
- json_string = json.dumps(data)
100
- with open(filepath, "a") as f:
101
- f.write(json_string + "\n")
102
-
13
+ @staticmethod
14
+ def read_csv(filepath: str) -> List[Dict[str, Any]]:
15
+ """
16
+ Reads a CSV file and returns its contents as a list of dictionaries.
17
+
18
+ Args:
19
+ filepath (str): The path to the CSV file to be read.
20
+
21
+ Returns:
22
+ List[Dict[str, Any]]: A list of dictionaries, each representing a row in the CSV file.
23
+ """
24
+ with open(filepath, 'r', newline='') as csv_file:
25
+ reader = csv.DictReader(csv_file)
26
+ return list(reader)
27
+
28
+ @staticmethod
29
+ def read_jsonl(filepath: str) -> List[Any]:
30
+ """
31
+ Reads a JSON Lines file and returns its contents as a list.
32
+
33
+ Args:
34
+ filepath (str): The path to the JSON Lines file to be read.
35
+
36
+ Returns:
37
+ List[Any]: A list where each element is a JSON object from a line in the file.
38
+ """
39
+ with open(filepath, 'r') as f:
40
+ return [json.loads(line) for line in f]
41
+
42
+ @staticmethod
43
+ def write_json(data: List[Dict[str, Any]], filepath: str) -> None:
44
+ """
45
+ Writes a list of dictionaries to a JSON file.
46
+
47
+ Args:
48
+ data (List[Dict[str, Any]]): The data to be written to the JSON file.
49
+ filepath (str): The path where the JSON file will be saved.
50
+
51
+ Returns:
52
+ None: This function does not return anything.
53
+ """
54
+ with open(filepath, 'w') as json_file:
55
+ json.dump(data, json_file, indent=4)
56
+
57
+ @staticmethod
58
+ def read_json(filepath: str) -> Any:
59
+ """
60
+ Reads a JSON file and returns its content.
61
+
62
+ Args:
63
+ filepath (str): The path to the JSON file to be read.
64
+
65
+ Returns:
66
+ Any: The content of the JSON file.
67
+ """
68
+ with open(filepath, 'r') as json_file:
69
+ return json.load(json_file)
70
+
71
+ @staticmethod
72
+ def merge_csv_files(filepaths: List[str], output_filepath: str) -> None:
73
+ """
74
+ Merges multiple CSV files into a single CSV file.
75
+
76
+ Args:
77
+ filepaths (List[str]): A list of file paths to the CSV files to be merged.
78
+ output_filepath (str): The path where the merged CSV file will be saved.
79
+
80
+ Returns:
81
+ None: This function does not return anything.
82
+ """
83
+ merged_data = []
84
+ fieldnames = set()
85
+ for filepath in filepaths:
86
+ with open(filepath, 'r', newline='') as csv_file:
87
+ reader = csv.DictReader(csv_file)
88
+ if reader.fieldnames is not None:
89
+ fieldnames.update(reader.fieldnames)
90
+ for row in reader:
91
+ merged_data.append(row)
92
+ fieldnames = list(fieldnames)
93
+ with open(output_filepath, 'w', newline='') as csv_file:
94
+ writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
95
+ writer.writeheader()
96
+ writer.writerows(merged_data)
97
+
98
+ @staticmethod
99
+ def to_csv(input: List[Dict[str, Any]],
100
+ filepath: str,
101
+ file_exist_ok: bool = False) -> None:
102
+ """
103
+ Writes a list of dictionaries to a CSV file.
104
+
105
+ Args:
106
+ input: A list of dictionaries where each dictionary represents a row in the CSV.
107
+ filepath: The path to the CSV file to write to.
108
+ file_exist_ok: If True, creates the directory for the file if it does not exist.
109
+
110
+ Raises:
111
+ FileNotFoundError: If the directory does not exist and file_exist_ok is False.
112
+
113
+ Examples:
114
+ >>> data = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
115
+ >>> to_csv(data, 'people.csv', file_exist_ok=True)
116
+ """
117
+ if not input:
118
+ return
119
+ if not os.path.exists(os.path.dirname(filepath)) and os.path.dirname(filepath) != '':
120
+ if file_exist_ok:
121
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
122
+ else:
123
+ raise FileNotFoundError(f"The directory {os.path.dirname(filepath)} does not exist.")
124
+
125
+ with open(filepath, 'w', newline='') as csv_file:
126
+ writer = csv.DictWriter(csv_file, fieldnames=input[0].keys())
127
+ writer.writeheader()
128
+ for row in input:
129
+ writer.writerow(row)
130
+
131
+ @staticmethod
132
+ def append_to_jsonl(data: Any, filepath: str) -> None:
133
+ """
134
+ Appends a data entry to a JSON Lines file.
135
+
136
+ Args:
137
+ data: The data to append, which can be any JSON serializable object.
138
+ filepath: The path to the JSON Lines file to append to.
139
+
140
+ Examples:
141
+ >>> append_to_jsonl({'name': 'Charlie', 'age': 35}, 'people.jsonl')
142
+ """
143
+ json_string = json.dumps(data)
144
+ with open(filepath, "a") as f:
145
+ f.write(json_string + "\n")
146
+
147
+ @staticmethod
148
+ def to_temp(input: Any) -> tempfile.NamedTemporaryFile:
149
+ """
150
+ Writes the given input to a temporary file in JSON format.
151
+
152
+ Args:
153
+ input: The input data to write to the file. Can be a string or an iterable.
154
+
155
+ Returns:
156
+ A NamedTemporaryFile object representing the temporary file.
157
+
158
+ Raises:
159
+ TypeError: If the input data is not JSON serializable.
160
+
161
+ Examples:
162
+ >>> temp_file = to_temp("test string")
163
+ >>> with open(temp_file.name, 'r') as file:
164
+ ... content = json.load(file)
165
+ >>> content
166
+ ["test string"]
167
+
168
+ >>> temp_file = to_temp(["test", "string"])
169
+ >>> with open(temp_file.name, 'r') as file:
170
+ ... content = json.load(file)
171
+ >>> content
172
+ ["test", "string"]
173
+ """
174
+ if isinstance(input, str):
175
+ input = [input]
176
+ elif isinstance(input, Iterable):
177
+ input = [item for item in input if item is not None]
178
+
179
+ temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
180
+ try:
181
+ json.dump(input, temp_file)
182
+ except TypeError as e:
183
+ temp_file.close() # Ensuring file closure before raising error
184
+ raise TypeError(f"Data provided is not JSON serializable: {e}")
185
+ temp_file.close()
186
+ return temp_file
187
+