cledar-sdk 2.0.2__py3-none-any.whl → 2.1.0__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.
Files changed (85) hide show
  1. cledar/__init__.py +1 -0
  2. cledar/kafka/README.md +239 -0
  3. cledar/kafka/__init__.py +42 -0
  4. cledar/kafka/clients/base.py +117 -0
  5. cledar/kafka/clients/consumer.py +138 -0
  6. cledar/kafka/clients/producer.py +97 -0
  7. cledar/kafka/config/schemas.py +262 -0
  8. cledar/kafka/exceptions.py +17 -0
  9. cledar/kafka/handlers/dead_letter.py +88 -0
  10. cledar/kafka/handlers/parser.py +83 -0
  11. cledar/kafka/logger.py +5 -0
  12. cledar/kafka/models/input.py +17 -0
  13. cledar/kafka/models/message.py +14 -0
  14. cledar/kafka/models/output.py +12 -0
  15. cledar/kafka/tests/.env.test.kafka +3 -0
  16. cledar/kafka/tests/README.md +216 -0
  17. cledar/kafka/tests/conftest.py +104 -0
  18. cledar/kafka/tests/integration/__init__.py +1 -0
  19. cledar/kafka/tests/integration/conftest.py +78 -0
  20. cledar/kafka/tests/integration/helpers.py +47 -0
  21. cledar/kafka/tests/integration/test_consumer_integration.py +375 -0
  22. cledar/kafka/tests/integration/test_integration.py +394 -0
  23. cledar/kafka/tests/integration/test_producer_consumer_interaction.py +388 -0
  24. cledar/kafka/tests/integration/test_producer_integration.py +217 -0
  25. cledar/kafka/tests/unit/__init__.py +1 -0
  26. cledar/kafka/tests/unit/test_base_kafka_client.py +391 -0
  27. cledar/kafka/tests/unit/test_config_validation.py +609 -0
  28. cledar/kafka/tests/unit/test_dead_letter_handler.py +443 -0
  29. cledar/kafka/tests/unit/test_error_handling.py +674 -0
  30. cledar/kafka/tests/unit/test_input_parser.py +310 -0
  31. cledar/kafka/tests/unit/test_input_parser_comprehensive.py +489 -0
  32. cledar/kafka/tests/unit/test_utils.py +25 -0
  33. cledar/kafka/tests/unit/test_utils_comprehensive.py +408 -0
  34. cledar/kafka/utils/callbacks.py +28 -0
  35. cledar/kafka/utils/messages.py +39 -0
  36. cledar/kafka/utils/topics.py +15 -0
  37. cledar/kserve/README.md +352 -0
  38. cledar/kserve/__init__.py +5 -0
  39. cledar/kserve/tests/__init__.py +0 -0
  40. cledar/kserve/tests/test_utils.py +64 -0
  41. cledar/kserve/utils.py +30 -0
  42. cledar/logging/README.md +53 -0
  43. cledar/logging/__init__.py +5 -0
  44. cledar/logging/tests/test_universal_plaintext_formatter.py +249 -0
  45. cledar/logging/universal_plaintext_formatter.py +99 -0
  46. cledar/monitoring/README.md +71 -0
  47. cledar/monitoring/__init__.py +5 -0
  48. cledar/monitoring/monitoring_server.py +156 -0
  49. cledar/monitoring/tests/integration/test_monitoring_server_int.py +162 -0
  50. cledar/monitoring/tests/test_monitoring_server.py +59 -0
  51. cledar/nonce/README.md +99 -0
  52. cledar/nonce/__init__.py +5 -0
  53. cledar/nonce/nonce_service.py +62 -0
  54. cledar/nonce/tests/__init__.py +0 -0
  55. cledar/nonce/tests/test_nonce_service.py +136 -0
  56. cledar/redis/README.md +536 -0
  57. cledar/redis/__init__.py +17 -0
  58. cledar/redis/async_example.py +112 -0
  59. cledar/redis/example.py +67 -0
  60. cledar/redis/exceptions.py +25 -0
  61. cledar/redis/logger.py +5 -0
  62. cledar/redis/model.py +14 -0
  63. cledar/redis/redis.py +764 -0
  64. cledar/redis/redis_config_store.py +333 -0
  65. cledar/redis/tests/test_async_integration_redis.py +158 -0
  66. cledar/redis/tests/test_async_redis_service.py +380 -0
  67. cledar/redis/tests/test_integration_redis.py +119 -0
  68. cledar/redis/tests/test_redis_service.py +319 -0
  69. cledar/storage/README.md +529 -0
  70. cledar/storage/__init__.py +6 -0
  71. cledar/storage/constants.py +5 -0
  72. cledar/storage/exceptions.py +79 -0
  73. cledar/storage/models.py +41 -0
  74. cledar/storage/object_storage.py +1274 -0
  75. cledar/storage/tests/conftest.py +18 -0
  76. cledar/storage/tests/test_abfs.py +164 -0
  77. cledar/storage/tests/test_integration_filesystem.py +359 -0
  78. cledar/storage/tests/test_integration_s3.py +453 -0
  79. cledar/storage/tests/test_local.py +384 -0
  80. cledar/storage/tests/test_s3.py +521 -0
  81. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/METADATA +1 -1
  82. cledar_sdk-2.1.0.dist-info/RECORD +84 -0
  83. cledar_sdk-2.0.2.dist-info/RECORD +0 -4
  84. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/WHEEL +0 -0
  85. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,384 @@
1
+ # mypy: disable-error-code=no-untyped-def
2
+ import io
3
+ from contextlib import contextmanager
4
+ from unittest.mock import MagicMock, patch
5
+
6
+ import pytest
7
+ from faker import Faker
8
+
9
+ from cledar.storage.exceptions import ReadFileError, UploadFileError
10
+ from cledar.storage.models import ObjectStorageServiceConfig
11
+ from cledar.storage.object_storage import ObjectStorageService
12
+
13
+ fake = Faker()
14
+
15
+
16
+ @pytest.fixture(name="object_storage_service")
17
+ @patch("fsspec.filesystem")
18
+ def fixture_object_storage_service(
19
+ fsspec_client: MagicMock, object_storage_config: ObjectStorageServiceConfig
20
+ ) -> ObjectStorageService:
21
+ fsspec_client.return_value = MagicMock()
22
+ return ObjectStorageService(object_storage_config)
23
+
24
+
25
+ @patch("fsspec.filesystem")
26
+ def test_init(
27
+ fsspec_client: MagicMock, object_storage_config: ObjectStorageServiceConfig
28
+ ) -> None:
29
+ ObjectStorageService(object_storage_config)
30
+
31
+ fsspec_client.assert_any_call(
32
+ "s3",
33
+ key=object_storage_config.s3_access_key,
34
+ secret=object_storage_config.s3_secret_key,
35
+ client_kwargs={"endpoint_url": object_storage_config.s3_endpoint_url},
36
+ max_concurrency=object_storage_config.s3_max_concurrency,
37
+ )
38
+ fsspec_client.assert_any_call(
39
+ "file",
40
+ )
41
+
42
+
43
+ def test_upload_buffer_local_success(
44
+ object_storage_service: ObjectStorageService,
45
+ ) -> None:
46
+ """
47
+ Test successful buffer upload to filesystem.
48
+ """
49
+ buffer_str = io.StringIO(fake.text())
50
+ buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
51
+ destination_path = fake.file_path()
52
+
53
+ mock_file = MagicMock()
54
+ mock_file.write = MagicMock()
55
+
56
+ @contextmanager
57
+ def open_cm(*_args: object, **_kwargs: object):
58
+ yield mock_file
59
+
60
+ object_storage_service.local_client.open = MagicMock(
61
+ side_effect=lambda *a, **k: open_cm()
62
+ )
63
+
64
+ object_storage_service.upload_buffer(
65
+ buffer=buffer_bytes, destination_path=destination_path
66
+ )
67
+
68
+ object_storage_service.local_client.open.assert_called_once_with(
69
+ path=destination_path, mode="wb"
70
+ )
71
+ mock_file.write.assert_called_once_with(buffer_bytes.getbuffer())
72
+
73
+
74
+ def test_upload_buffer_local_exception(
75
+ object_storage_service: ObjectStorageService,
76
+ ) -> None:
77
+ """
78
+ Test buffer upload to filesystem with write exception.
79
+ """
80
+ buffer_str = io.StringIO(fake.text())
81
+ buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
82
+ destination_path = fake.file_path()
83
+
84
+ class Writer:
85
+ def write(self, _b: bytes) -> None:
86
+ raise UploadFileError
87
+
88
+ @contextmanager
89
+ def open_cm(*_args: object, **_kwargs: object):
90
+ yield Writer()
91
+
92
+ object_storage_service.local_client.open = MagicMock(
93
+ side_effect=lambda *a, **k: open_cm()
94
+ )
95
+
96
+ with pytest.raises(UploadFileError):
97
+ object_storage_service.upload_buffer(
98
+ buffer=buffer_bytes, destination_path=destination_path
99
+ )
100
+ object_storage_service.local_client.open.assert_called_once()
101
+
102
+
103
+ def test_upload_buffer_local_missing_params(
104
+ object_storage_service: ObjectStorageService,
105
+ ) -> None:
106
+ """
107
+ Test buffer upload with missing parameters.
108
+ """
109
+ buffer_str = io.StringIO(fake.text())
110
+ buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
111
+
112
+ with pytest.raises(
113
+ ValueError, match="Either destination_path or bucket and key must be provided"
114
+ ):
115
+ object_storage_service.upload_buffer(buffer=buffer_bytes)
116
+
117
+
118
+ def test_read_file_local_success(
119
+ object_storage_service: ObjectStorageService,
120
+ ) -> None:
121
+ """
122
+ Test successful file read from filesystem.
123
+ """
124
+ path = fake.file_path()
125
+ expected_content = fake.text().encode()
126
+
127
+ mock_file = MagicMock()
128
+ mock_file.read.return_value = expected_content
129
+
130
+ @contextmanager
131
+ def open_cm(*_args: object, **_kwargs: object):
132
+ yield mock_file
133
+
134
+ object_storage_service.local_client.open = MagicMock(
135
+ side_effect=lambda *a, **k: open_cm()
136
+ )
137
+
138
+ result = object_storage_service.read_file(path=path)
139
+ assert result == expected_content
140
+ object_storage_service.local_client.open.assert_called_once_with(
141
+ path=path, mode="rb"
142
+ )
143
+
144
+
145
+ def test_read_file_local_retry_mechanism(
146
+ object_storage_service: ObjectStorageService,
147
+ ) -> None:
148
+ """
149
+ Test file read retry mechanism on filesystem errors.
150
+ """
151
+ path = fake.file_path()
152
+ expected_content = fake.text().encode()
153
+
154
+ mock_file = MagicMock()
155
+ mock_file.read.return_value = expected_content
156
+
157
+ @contextmanager
158
+ def open_cm(*_args: object, **_kwargs: object):
159
+ yield mock_file
160
+
161
+ call_count = 0
162
+
163
+ def side_effect(*_args, **_kwargs):
164
+ nonlocal call_count
165
+ call_count += 1
166
+ if call_count <= 2:
167
+ raise OSError("Temporary error")
168
+ return open_cm()
169
+
170
+ object_storage_service.local_client.open = MagicMock(side_effect=side_effect)
171
+
172
+ result = object_storage_service.read_file(path=path, max_tries=3)
173
+
174
+ assert result == expected_content
175
+ assert call_count == 3
176
+
177
+
178
+ def test_read_file_local_max_retries_exceeded(
179
+ object_storage_service: ObjectStorageService,
180
+ ) -> None:
181
+ """
182
+ Test file read when max retries are exceeded.
183
+ """
184
+ path = fake.file_path()
185
+
186
+ @contextmanager
187
+ def open_cm(*_args: object, **_kwargs: object):
188
+ raise ReadFileError
189
+
190
+ object_storage_service.local_client.open = MagicMock(
191
+ side_effect=lambda *a, **k: open_cm()
192
+ )
193
+
194
+ with pytest.raises(ReadFileError):
195
+ object_storage_service.read_file(path=path, max_tries=2)
196
+
197
+
198
+ def test_read_file_local_missing_params(
199
+ object_storage_service: ObjectStorageService,
200
+ ) -> None:
201
+ """
202
+ Test file read with missing parameters.
203
+ """
204
+ with pytest.raises(
205
+ ValueError, match="Either path or bucket and key must be provided"
206
+ ):
207
+ object_storage_service.read_file()
208
+
209
+
210
+ def test_upload_file_local_success(
211
+ object_storage_service: ObjectStorageService,
212
+ ) -> None:
213
+ """
214
+ Test successful file upload to filesystem.
215
+ """
216
+ file_path = fake.file_path()
217
+ destination_path = fake.file_path()
218
+
219
+ object_storage_service.local_client.put = MagicMock()
220
+
221
+ object_storage_service.upload_file(
222
+ file_path=file_path, destination_path=destination_path
223
+ )
224
+
225
+ object_storage_service.local_client.put.assert_called_once_with(
226
+ lpath=file_path, rpath=destination_path
227
+ )
228
+
229
+
230
+ def test_upload_file_local_exception(
231
+ object_storage_service: ObjectStorageService,
232
+ ) -> None:
233
+ """
234
+ Test file upload to filesystem with exception.
235
+ """
236
+ file_path = fake.file_path()
237
+ destination_path = fake.file_path()
238
+
239
+ object_storage_service.local_client.put.side_effect = UploadFileError
240
+
241
+ with pytest.raises(UploadFileError):
242
+ object_storage_service.upload_file(
243
+ file_path=file_path, destination_path=destination_path
244
+ )
245
+
246
+ object_storage_service.local_client.put.assert_called_once_with(
247
+ lpath=file_path, rpath=destination_path
248
+ )
249
+
250
+
251
+ def test_upload_file_local_missing_params(
252
+ object_storage_service: ObjectStorageService,
253
+ ) -> None:
254
+ """
255
+ Test file upload with missing parameters.
256
+ """
257
+ file_path = fake.file_path()
258
+
259
+ with pytest.raises(
260
+ ValueError, match="Either destination_path or bucket and key must be provided"
261
+ ):
262
+ object_storage_service.upload_file(file_path=file_path)
263
+
264
+
265
+ def test_list_objects_recursive_local(
266
+ object_storage_service: ObjectStorageService,
267
+ ) -> None:
268
+ path = "/tmp/test"
269
+ mock_objects = [
270
+ "/tmp/test/file1.txt",
271
+ "/tmp/test/file2.txt",
272
+ "/tmp/test/subfolder/file3.txt",
273
+ ]
274
+ object_storage_service.local_client.find.return_value = mock_objects
275
+
276
+ result = object_storage_service.list_objects(path=path, recursive=True)
277
+
278
+ assert len(result) == 3
279
+ assert "/tmp/test/file1.txt" in result
280
+ object_storage_service.local_client.find.assert_called_once()
281
+
282
+
283
+ def test_list_objects_non_recursive_local(
284
+ object_storage_service: ObjectStorageService,
285
+ ) -> None:
286
+ path = "/tmp/test"
287
+ mock_objects = ["/tmp/test/file1.txt", "/tmp/test/file2.txt"]
288
+ object_storage_service.local_client.ls.return_value = mock_objects
289
+
290
+ result = object_storage_service.list_objects(path=path, recursive=False)
291
+
292
+ assert len(result) == 2
293
+ object_storage_service.local_client.ls.assert_called_once()
294
+
295
+
296
+ def test_delete_file_local(object_storage_service: ObjectStorageService) -> None:
297
+ path = "/tmp/test/file.txt"
298
+
299
+ object_storage_service.delete_file(path=path)
300
+
301
+ object_storage_service.local_client.rm.assert_called_once_with(path)
302
+
303
+
304
+ def test_file_exists_true_local(object_storage_service: ObjectStorageService) -> None:
305
+ path = "/tmp/test/file.txt"
306
+ object_storage_service.local_client.exists.return_value = True
307
+
308
+ result = object_storage_service.file_exists(path=path)
309
+
310
+ assert result is True
311
+ object_storage_service.local_client.exists.assert_called_once_with(path)
312
+
313
+
314
+ def test_file_exists_false_local(object_storage_service: ObjectStorageService) -> None:
315
+ path = "/tmp/test/file.txt"
316
+ object_storage_service.local_client.exists.return_value = False
317
+
318
+ result = object_storage_service.file_exists(path=path)
319
+
320
+ assert result is False
321
+ object_storage_service.local_client.exists.assert_called_once_with(path)
322
+
323
+
324
+ def test_download_file_local(object_storage_service: ObjectStorageService) -> None:
325
+ source_path = "/tmp/source/file.txt"
326
+ dest_path = "/tmp/dest/file.txt"
327
+
328
+ object_storage_service.download_file(dest_path, source_path=source_path)
329
+
330
+ object_storage_service.local_client.get.assert_called_once_with(
331
+ source_path, dest_path
332
+ )
333
+
334
+
335
+ def test_get_file_size_local(object_storage_service: ObjectStorageService) -> None:
336
+ path = "/tmp/test/file.txt"
337
+ expected_size = 2048
338
+ object_storage_service.local_client.info.return_value = {"size": expected_size}
339
+
340
+ result = object_storage_service.get_file_size(path=path)
341
+
342
+ assert result == expected_size
343
+ object_storage_service.local_client.info.assert_called_once_with(path)
344
+
345
+
346
+ def test_get_file_info_local(object_storage_service: ObjectStorageService) -> None:
347
+ path = "/tmp/test/file.txt"
348
+ expected_info = {
349
+ "size": 2048,
350
+ "mtime": 1234567890,
351
+ "type": "file",
352
+ }
353
+ object_storage_service.local_client.info.return_value = expected_info
354
+
355
+ result = object_storage_service.get_file_info(path=path)
356
+
357
+ assert result == expected_info
358
+ object_storage_service.local_client.info.assert_called_once_with(path)
359
+
360
+
361
+ def test_copy_file_local_to_local(
362
+ object_storage_service: ObjectStorageService,
363
+ ) -> None:
364
+ source_path = "/tmp/source/file.txt"
365
+ dest_path = "/tmp/dest/file.txt"
366
+
367
+ object_storage_service.copy_file(source_path=source_path, dest_path=dest_path)
368
+
369
+ object_storage_service.local_client.copy.assert_called_once_with(
370
+ source_path, dest_path
371
+ )
372
+
373
+
374
+ def test_move_file_local_to_local(
375
+ object_storage_service: ObjectStorageService,
376
+ ) -> None:
377
+ source_path = "/tmp/source/file.txt"
378
+ dest_path = "/tmp/dest/file.txt"
379
+
380
+ object_storage_service.move_file(source_path=source_path, dest_path=dest_path)
381
+
382
+ object_storage_service.local_client.move.assert_called_once_with(
383
+ source_path, dest_path
384
+ )