cledar-sdk 2.0.3__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 (41) hide show
  1. cledar/__init__.py +1 -0
  2. cledar/kafka/__init__.py +2 -0
  3. cledar/kafka/clients/base.py +24 -5
  4. cledar/kafka/clients/consumer.py +28 -0
  5. cledar/kafka/clients/producer.py +17 -0
  6. cledar/kafka/config/schemas.py +91 -7
  7. cledar/kafka/exceptions.py +7 -12
  8. cledar/kafka/handlers/dead_letter.py +26 -20
  9. cledar/kafka/handlers/parser.py +36 -2
  10. cledar/kafka/logger.py +2 -0
  11. cledar/kafka/models/input.py +4 -0
  12. cledar/kafka/models/message.py +4 -0
  13. cledar/kafka/models/output.py +4 -0
  14. cledar/kafka/utils/callbacks.py +9 -0
  15. cledar/kafka/utils/messages.py +11 -0
  16. cledar/kafka/utils/topics.py +13 -0
  17. cledar/kserve/__init__.py +2 -0
  18. cledar/kserve/utils.py +3 -0
  19. cledar/logging/__init__.py +2 -0
  20. cledar/logging/universal_plaintext_formatter.py +17 -12
  21. cledar/monitoring/__init__.py +2 -0
  22. cledar/monitoring/monitoring_server.py +45 -1
  23. cledar/nonce/__init__.py +2 -0
  24. cledar/nonce/nonce_service.py +30 -4
  25. cledar/redis/__init__.py +2 -0
  26. cledar/redis/async_example.py +4 -3
  27. cledar/redis/example.py +30 -0
  28. cledar/redis/exceptions.py +3 -0
  29. cledar/redis/logger.py +2 -0
  30. cledar/redis/model.py +4 -0
  31. cledar/redis/redis.py +252 -13
  32. cledar/redis/redis_config_store.py +81 -0
  33. cledar/storage/__init__.py +2 -0
  34. cledar/storage/constants.py +2 -0
  35. cledar/storage/exceptions.py +29 -0
  36. cledar/storage/models.py +22 -0
  37. cledar/storage/object_storage.py +342 -23
  38. {cledar_sdk-2.0.3.dist-info → cledar_sdk-2.1.0.dist-info}/METADATA +1 -1
  39. {cledar_sdk-2.0.3.dist-info → cledar_sdk-2.1.0.dist-info}/RECORD +41 -41
  40. {cledar_sdk-2.0.3.dist-info → cledar_sdk-2.1.0.dist-info}/WHEEL +0 -0
  41. {cledar_sdk-2.0.3.dist-info → cledar_sdk-2.1.0.dist-info}/licenses/LICENSE +0 -0
cledar/redis/redis.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Redis service implementations for the Cledar SDK."""
2
+
1
3
  import json
2
4
  import logging
3
5
  from dataclasses import dataclass
@@ -20,11 +22,18 @@ logger = logging.getLogger("redis_service")
20
22
 
21
23
 
22
24
  class CustomEncoder(json.JSONEncoder):
23
- """
24
- Custom JSON encoder that can handle Enum objects and datetime objects.
25
- """
25
+ """Custom JSON encoder that can handle Enum objects and datetime objects."""
26
26
 
27
27
  def default(self, o: Any) -> Any:
28
+ """Process objects for JSON encoding.
29
+
30
+ Args:
31
+ o: The object to encode.
32
+
33
+ Returns:
34
+ Any: The encoded object.
35
+
36
+ """
28
37
  if isinstance(o, Enum):
29
38
  return o.name.lower()
30
39
  if isinstance(o, datetime):
@@ -37,12 +46,16 @@ T = TypeVar("T", bound=BaseModel)
37
46
 
38
47
  @dataclass
39
48
  class FailedValue:
49
+ """Represents a failed Redis operation for a specific key."""
50
+
40
51
  key: str
41
52
  error: Exception
42
53
 
43
54
 
44
55
  @dataclass
45
56
  class RedisServiceConfig:
57
+ """Configuration for Redis services."""
58
+
46
59
  redis_host: str
47
60
  redis_port: int
48
61
  redis_db: int = 0
@@ -50,12 +63,26 @@ class RedisServiceConfig:
50
63
 
51
64
 
52
65
  class RedisService:
66
+ """Synchronous Redis service with Pydantic model support."""
67
+
53
68
  def __init__(self, config: RedisServiceConfig):
69
+ """Initialize the synchronous Redis service.
70
+
71
+ Args:
72
+ config: The service configuration.
73
+
74
+ """
54
75
  self.config = config
55
76
  self._client: redis.Redis
56
77
  self.connect()
57
78
 
58
79
  def connect(self) -> None:
80
+ """Establish a connection to Redis.
81
+
82
+ Raises:
83
+ RedisConnectionError: If the connection fails.
84
+
85
+ """
59
86
  try:
60
87
  self._client = redis.Redis(
61
88
  host=self.config.redis_host,
@@ -82,6 +109,12 @@ class RedisService:
82
109
  raise RedisConnectionError("Could not initialize Redis client") from exc
83
110
 
84
111
  def is_alive(self) -> bool:
112
+ """Check if the Redis connection is alive.
113
+
114
+ Returns:
115
+ bool: True if connection is alive, False otherwise.
116
+
117
+ """
85
118
  try:
86
119
  return bool(self._client.ping())
87
120
  except redis.ConnectionError:
@@ -92,9 +125,17 @@ class RedisService:
92
125
  return False
93
126
 
94
127
  def _prepare_for_serialization(self, value: Any) -> Any:
95
- """
128
+ """Process data structures for serialization.
129
+
96
130
  Recursively process data structures, converting BaseModel instances to
97
131
  serializable dicts.
132
+
133
+ Args:
134
+ value: The value to process.
135
+
136
+ Returns:
137
+ Any: The processed value.
138
+
98
139
  """
99
140
  if isinstance(value, BaseModel):
100
141
  return value.model_dump()
@@ -105,6 +146,22 @@ class RedisService:
105
146
  return value
106
147
 
107
148
  def set(self, key: str, value: Any) -> bool:
149
+ """Set a value in Redis.
150
+
151
+ Args:
152
+ key: The key to set.
153
+ value: The value to set.
154
+
155
+ Returns:
156
+ bool: True if successful, False otherwise.
157
+
158
+ Raises:
159
+ ValueError: If the key is not a string.
160
+ RedisSerializationError: If serialization fails.
161
+ RedisConnectionError: If the connection fails.
162
+ RedisOperationError: If the operation fails.
163
+
164
+ """
108
165
  if not isinstance(key, str):
109
166
  raise ValueError(f"Key must be a string, got {type(key)}")
110
167
  if value is None:
@@ -139,6 +196,22 @@ class RedisService:
139
196
  raise RedisOperationError(f"Failed to set key '{key}'") from exc
140
197
 
141
198
  def get(self, key: str, model: type[T]) -> T | None:
199
+ """Get a value from Redis and validate it against a model.
200
+
201
+ Args:
202
+ key: The key to fetch.
203
+ model: The Pydantic model to validate against.
204
+
205
+ Returns:
206
+ T | None: The validated model or None if key does not exist.
207
+
208
+ Raises:
209
+ ValueError: If the key is not a string.
210
+ RedisDeserializationError: If decoding or validation fails.
211
+ RedisConnectionError: If the connection fails.
212
+ RedisOperationError: If the operation fails.
213
+
214
+ """
142
215
  if not isinstance(key, str):
143
216
  raise ValueError(f"Key must be a string, got {type(key)}")
144
217
 
@@ -175,6 +248,20 @@ class RedisService:
175
248
  raise RedisOperationError(f"Failed to get key '{key}'") from exc
176
249
 
177
250
  def get_raw(self, key: str) -> Any | None:
251
+ """Get a raw value from Redis.
252
+
253
+ Args:
254
+ key: The key to fetch.
255
+
256
+ Returns:
257
+ Any | None: The raw value or None if key does not exist.
258
+
259
+ Raises:
260
+ ValueError: If the key is not a string.
261
+ RedisConnectionError: If the connection fails.
262
+ RedisOperationError: If the operation fails.
263
+
264
+ """
178
265
  if not isinstance(key, str):
179
266
  raise ValueError(f"Key must be a string, got {type(key)}")
180
267
 
@@ -195,6 +282,20 @@ class RedisService:
195
282
  raise RedisOperationError(f"Failed to get key '{key}'") from exc
196
283
 
197
284
  def list_keys(self, pattern: str) -> list[str]:
285
+ """List all keys matching a pattern.
286
+
287
+ Args:
288
+ pattern: The pattern to match.
289
+
290
+ Returns:
291
+ list[str]: A list of matching keys.
292
+
293
+ Raises:
294
+ ValueError: If the pattern is not a string.
295
+ RedisConnectionError: If the connection fails.
296
+ RedisOperationError: If the operation fails.
297
+
298
+ """
198
299
  if not isinstance(pattern, str):
199
300
  raise ValueError(f"Pattern must be a string, got {type(pattern)}")
200
301
 
@@ -215,6 +316,22 @@ class RedisService:
215
316
  ) from exc
216
317
 
217
318
  def mget(self, keys: list[str], model: type[T]) -> list[T | None | FailedValue]:
319
+ """Get multiple values from Redis and validate them against a model.
320
+
321
+ Args:
322
+ keys: A list of keys to fetch.
323
+ model: The Pydantic model to validate against.
324
+
325
+ Returns:
326
+ list[T | None | FailedValue]: A list of validated models, None for
327
+ missing keys, or FailedValue for items that failed validation.
328
+
329
+ Raises:
330
+ ValueError: If the keys are not a list.
331
+ RedisConnectionError: If the connection fails.
332
+ RedisOperationError: If the operation fails.
333
+
334
+ """
218
335
  if not isinstance(keys, list):
219
336
  raise ValueError(f"Keys must be a list, got {type(keys)}")
220
337
 
@@ -260,6 +377,20 @@ class RedisService:
260
377
  raise RedisOperationError("Failed to mget keys") from exc
261
378
 
262
379
  def delete(self, key: str) -> bool:
380
+ """Delete a key from Redis.
381
+
382
+ Args:
383
+ key: The key to delete.
384
+
385
+ Returns:
386
+ bool: True if the key was deleted, False otherwise.
387
+
388
+ Raises:
389
+ ValueError: If the key is not a string.
390
+ RedisConnectionError: If the connection fails.
391
+ RedisOperationError: If the operation fails.
392
+
393
+ """
263
394
  if not isinstance(key, str):
264
395
  raise ValueError(f"Key must be a string, got {type(key)}")
265
396
 
@@ -283,11 +414,22 @@ class AsyncRedisService:
283
414
  """Asynchronous Redis service with async/await support."""
284
415
 
285
416
  def __init__(self, config: RedisServiceConfig):
417
+ """Initialize the asynchronous Redis service.
418
+
419
+ Args:
420
+ config: The service configuration.
421
+
422
+ """
286
423
  self.config = config
287
424
  self._client: aioredis.Redis
288
425
 
289
426
  async def connect(self) -> None:
290
- """Establish connection to Redis asynchronously."""
427
+ """Establish connection to Redis asynchronously.
428
+
429
+ Raises:
430
+ RedisConnectionError: If the connection fails.
431
+
432
+ """
291
433
  try:
292
434
  self._client = aioredis.Redis(
293
435
  host=self.config.redis_host,
@@ -319,7 +461,12 @@ class AsyncRedisService:
319
461
  logger.info("Async Redis client closed.")
320
462
 
321
463
  async def is_alive(self) -> bool:
322
- """Check if Redis connection is alive."""
464
+ """Check if Redis connection is alive.
465
+
466
+ Returns:
467
+ bool: True if the connection is alive, False otherwise.
468
+
469
+ """
323
470
  try:
324
471
  return bool(await self._client.ping())
325
472
  except aioredis.ConnectionError:
@@ -330,9 +477,17 @@ class AsyncRedisService:
330
477
  return False
331
478
 
332
479
  def _prepare_for_serialization(self, value: Any) -> Any:
333
- """
480
+ """Process data structures for serialization.
481
+
334
482
  Recursively process data structures, converting BaseModel instances to
335
483
  serializable dicts.
484
+
485
+ Args:
486
+ value: The value to process.
487
+
488
+ Returns:
489
+ Any: The processed value.
490
+
336
491
  """
337
492
  if isinstance(value, BaseModel):
338
493
  return value.model_dump()
@@ -343,7 +498,22 @@ class AsyncRedisService:
343
498
  return value
344
499
 
345
500
  async def set(self, key: str, value: Any) -> bool:
346
- """Set a key-value pair in Redis."""
501
+ """Set a key-value pair in Redis.
502
+
503
+ Args:
504
+ key: The key to set.
505
+ value: The value to set.
506
+
507
+ Returns:
508
+ bool: True if successful, False otherwise.
509
+
510
+ Raises:
511
+ ValueError: If the key is not a string.
512
+ RedisSerializationError: If serialization fails.
513
+ RedisConnectionError: If the connection fails.
514
+ RedisOperationError: If the operation fails.
515
+
516
+ """
347
517
  if not isinstance(key, str):
348
518
  raise ValueError(f"Key must be a string, got {type(key)}")
349
519
  if value is None:
@@ -378,7 +548,22 @@ class AsyncRedisService:
378
548
  raise RedisOperationError(f"Failed to set key '{key}'") from exc
379
549
 
380
550
  async def get(self, key: str, model: type[T]) -> T | None:
381
- """Get a value from Redis and validate it against a Pydantic model."""
551
+ """Get a value from Redis and validate it against a Pydantic model.
552
+
553
+ Args:
554
+ key: The key to fetch.
555
+ model: The Pydantic model to validate against.
556
+
557
+ Returns:
558
+ T | None: The validated model or None if key does not exist.
559
+
560
+ Raises:
561
+ ValueError: If the key is not a string.
562
+ RedisDeserializationError: If decoding or validation fails.
563
+ RedisConnectionError: If the connection fails.
564
+ RedisOperationError: If the operation fails.
565
+
566
+ """
382
567
  if not isinstance(key, str):
383
568
  raise ValueError(f"Key must be a string, got {type(key)}")
384
569
 
@@ -415,7 +600,20 @@ class AsyncRedisService:
415
600
  raise RedisOperationError(f"Failed to get key '{key}'") from exc
416
601
 
417
602
  async def get_raw(self, key: str) -> Any | None:
418
- """Get a raw value from Redis without deserialization."""
603
+ """Get a raw value from Redis without deserialization.
604
+
605
+ Args:
606
+ key: The key to fetch.
607
+
608
+ Returns:
609
+ Any | None: The raw value or None if key does not exist.
610
+
611
+ Raises:
612
+ ValueError: If the key is not a string.
613
+ RedisConnectionError: If the connection fails.
614
+ RedisOperationError: If the operation fails.
615
+
616
+ """
419
617
  if not isinstance(key, str):
420
618
  raise ValueError(f"Key must be a string, got {type(key)}")
421
619
 
@@ -436,7 +634,20 @@ class AsyncRedisService:
436
634
  raise RedisOperationError(f"Failed to get key '{key}'") from exc
437
635
 
438
636
  async def list_keys(self, pattern: str) -> list[str]:
439
- """List keys matching a pattern."""
637
+ """List keys matching a pattern.
638
+
639
+ Args:
640
+ pattern: The pattern to match.
641
+
642
+ Returns:
643
+ list[str]: A list of matching keys.
644
+
645
+ Raises:
646
+ ValueError: If the pattern is not a string.
647
+ RedisConnectionError: If the connection fails.
648
+ RedisOperationError: If the operation fails.
649
+
650
+ """
440
651
  if not isinstance(pattern, str):
441
652
  raise ValueError(f"Pattern must be a string, got {type(pattern)}")
442
653
 
@@ -459,7 +670,22 @@ class AsyncRedisService:
459
670
  async def mget(
460
671
  self, keys: list[str], model: type[T]
461
672
  ) -> list[T | None | FailedValue]:
462
- """Get multiple values from Redis."""
673
+ """Get multiple values from Redis.
674
+
675
+ Args:
676
+ keys: A list of keys to fetch.
677
+ model: The Pydantic model to validate against.
678
+
679
+ Returns:
680
+ list[T | None | FailedValue]: A list of validated models, None for
681
+ missing keys, or FailedValue for items that failed validation.
682
+
683
+ Raises:
684
+ ValueError: If the keys are not a list.
685
+ RedisConnectionError: If the connection fails.
686
+ RedisOperationError: If the operation fails.
687
+
688
+ """
463
689
  if not isinstance(keys, list):
464
690
  raise ValueError(f"Keys must be a list, got {type(keys)}")
465
691
 
@@ -505,7 +731,20 @@ class AsyncRedisService:
505
731
  raise RedisOperationError("Failed to mget keys") from exc
506
732
 
507
733
  async def delete(self, key: str) -> bool:
508
- """Delete a key from Redis."""
734
+ """Delete a key from Redis.
735
+
736
+ Args:
737
+ key: The key to delete.
738
+
739
+ Returns:
740
+ bool: True if the key was deleted, False otherwise.
741
+
742
+ Raises:
743
+ ValueError: If the key is not a string.
744
+ RedisConnectionError: If the connection fails.
745
+ RedisOperationError: If the operation fails.
746
+
747
+ """
509
748
  if not isinstance(key, str):
510
749
  raise ValueError(f"Key must be a string, got {type(key)}")
511
750
 
@@ -1,3 +1,5 @@
1
+ """Redis-based configuration store with caching and watching capabilities."""
2
+
1
3
  import json
2
4
  import re
3
5
  import time
@@ -19,6 +21,11 @@ OP_EVENT_FORMAT = "__keyevent@{DB}__:{OPERATION}"
19
21
 
20
22
 
21
23
  class RedisConfigStore:
24
+ """Store for configuration objects in Redis with local caching and pub/sub.
25
+
26
+ Provides updates on configuration changes.
27
+ """
28
+
22
29
  TYPE_NONE = "none"
23
30
  TYPE_LIST = "list"
24
31
  TYPE_STRING = "string"
@@ -29,6 +36,13 @@ class RedisConfigStore:
29
36
  EVENT_LSET = "lset"
30
37
 
31
38
  def __init__(self, redis: Redis, prefix: str | None = None) -> None:
39
+ """Initialize the RedisConfigStore.
40
+
41
+ Args:
42
+ redis: An initialized Redis client.
43
+ prefix: Optional prefix for keys stored in Redis.
44
+
45
+ """
32
46
  self._redis: Redis = redis
33
47
  self._pubsub = redis.pubsub() # type: ignore
34
48
  self._db: int = redis.connection_pool.connection_kwargs.get("db")
@@ -42,18 +56,52 @@ class RedisConfigStore:
42
56
  self._watcher_thread.start()
43
57
 
44
58
  def is_ready(self) -> bool:
59
+ """Check if the Redis connection is ready.
60
+
61
+ Returns:
62
+ bool: True if Redis responds to ping, False otherwise.
63
+
64
+ """
45
65
  try:
46
66
  return self._redis.ping() # type: ignore
47
67
  except RedisConnectionError:
48
68
  return False
49
69
 
50
70
  def versions(self, key: str) -> int | None:
71
+ """Get the version (e.g., list length or existence) of a key.
72
+
73
+ Args:
74
+ key: The key to check.
75
+
76
+ Returns:
77
+ int | None: The version of the key.
78
+
79
+ """
51
80
  return self._key_versions(key)
52
81
 
53
82
  def cached_version(self, key: str) -> int | None:
83
+ """Get the version of a key stored in the local cache.
84
+
85
+ Args:
86
+ key: The key to check.
87
+
88
+ Returns:
89
+ int | None: The cached version of the key.
90
+
91
+ """
54
92
  return self._cache_verisons.get(key)
55
93
 
56
94
  def fetch(self, cls: type[T], key: str) -> T | None:
95
+ """Fetch a configuration object from Redis or local cache.
96
+
97
+ Args:
98
+ cls: The class to instantiate with the fetched data.
99
+ key: The key associated with the configuration.
100
+
101
+ Returns:
102
+ T | None: The configuration object, or None if not found.
103
+
104
+ """
57
105
  if key not in self._cache:
58
106
  new_value = self._key_fetch(key)
59
107
  if new_value is None:
@@ -64,11 +112,24 @@ class RedisConfigStore:
64
112
  return cls(**json.loads(self._cache[key]))
65
113
 
66
114
  def update(self, key: str, value: T) -> None:
115
+ """Update a configuration object in Redis and local cache.
116
+
117
+ Args:
118
+ key: The key to update.
119
+ value: The configuration object to store.
120
+
121
+ """
67
122
  self._cache[key] = self._key_update(key, value)
68
123
  self._cache_verisons[key] = self._key_versions(key) or -1
69
124
  self._key_watch(key)
70
125
 
71
126
  def delete(self, key: str) -> None:
127
+ """Delete a configuration object from Redis and local cache.
128
+
129
+ Args:
130
+ key: The key to delete.
131
+
132
+ """
72
133
  if key in self._cache:
73
134
  del self._cache[key]
74
135
  del self._cache_verisons[key]
@@ -78,12 +139,32 @@ class RedisConfigStore:
78
139
  def watch(
79
140
  self, key: str, callback: Callable[[int, str, str, str], None] | None = None
80
141
  ) -> None:
142
+ """Watch a key for changes and execute a callback.
143
+
144
+ Args:
145
+ key: The key to watch.
146
+ callback: The callback function to execute on change.
147
+
148
+ """
81
149
  self._key_watch(key, callback)
82
150
 
83
151
  def __setitem__(self, key: str, value: T) -> None:
152
+ """Alias for update method.
153
+
154
+ Args:
155
+ key: The key to update.
156
+ value: The value to set.
157
+
158
+ """
84
159
  self.update(key, value)
85
160
 
86
161
  def __delitem__(self, key: str) -> None:
162
+ """Alias for delete method.
163
+
164
+ Args:
165
+ key: The key to delete.
166
+
167
+ """
87
168
  self.delete(key)
88
169
 
89
170
  def _key_watch(
@@ -1,3 +1,5 @@
1
+ """Object storage module for handling S3, ABFS, and local filesystem operations."""
2
+
1
3
  from .models import ObjectStorageServiceConfig
2
4
  from .object_storage import ObjectStorageService
3
5
 
@@ -1,3 +1,5 @@
1
+ """Constants for object storage path prefixes."""
2
+
1
3
  S3_PATH_PREFIX = "s3://"
2
4
  ABFS_PATH_PREFIX = "abfs://"
3
5
  ABFSS_PATH_PREFIX = "abfss://"
@@ -1,50 +1,79 @@
1
+ """Exceptions for object storage operations."""
2
+
3
+
1
4
  class ObjectStorageError(Exception):
5
+ """Base exception for object storage errors."""
6
+
2
7
  pass
3
8
 
4
9
 
5
10
  class RequiredBucketNotFoundError(ObjectStorageError):
11
+ """Exception raised when a required bucket is not found."""
12
+
6
13
  pass
7
14
 
8
15
 
9
16
  class UploadBufferError(ObjectStorageError):
17
+ """Exception raised when uploading a buffer fails."""
18
+
10
19
  pass
11
20
 
12
21
 
13
22
  class UploadFileError(ObjectStorageError):
23
+ """Exception raised when uploading a file fails."""
24
+
14
25
  pass
15
26
 
16
27
 
17
28
  class ReadFileError(ObjectStorageError):
29
+ """Exception raised when reading a file fails."""
30
+
18
31
  pass
19
32
 
20
33
 
21
34
  class DownloadFileError(ObjectStorageError):
35
+ """Exception raised when downloading a file fails."""
36
+
22
37
  pass
23
38
 
24
39
 
25
40
  class ListObjectsError(ObjectStorageError):
41
+ """Exception raised when listing objects fails."""
42
+
26
43
  pass
27
44
 
28
45
 
29
46
  class DeleteFileError(ObjectStorageError):
47
+ """Exception raised when deleting a file fails."""
48
+
30
49
  pass
31
50
 
32
51
 
33
52
  class GetFileSizeError(ObjectStorageError):
53
+ """Exception raised when getting file size fails."""
54
+
34
55
  pass
35
56
 
36
57
 
37
58
  class GetFileInfoError(ObjectStorageError):
59
+ """Exception raised when getting file info fails."""
60
+
38
61
  pass
39
62
 
40
63
 
41
64
  class CopyFileError(ObjectStorageError):
65
+ """Exception raised when copying a file fails."""
66
+
42
67
  pass
43
68
 
44
69
 
45
70
  class MoveFileError(ObjectStorageError):
71
+ """Exception raised when moving a file fails."""
72
+
46
73
  pass
47
74
 
48
75
 
49
76
  class CheckFileExistenceError(ObjectStorageError):
77
+ """Exception raised when checking file existence fails."""
78
+
50
79
  pass
cledar/storage/models.py CHANGED
@@ -1,9 +1,23 @@
1
+ """Models for object storage configuration and transfer paths."""
2
+
1
3
  from typing import Literal
2
4
 
3
5
  from pydantic import BaseModel
4
6
 
5
7
 
6
8
  class ObjectStorageServiceConfig(BaseModel):
9
+ """Configuration for object storage service.
10
+
11
+ Attributes:
12
+ s3_endpoint_url: S3 endpoint URL for custom S3-compatible services.
13
+ s3_access_key: S3 access key for authentication.
14
+ s3_secret_key: S3 secret key for authentication.
15
+ s3_max_concurrency: Maximum number of concurrent S3 operations.
16
+ azure_account_name: Azure storage account name.
17
+ azure_account_key: Azure storage account key.
18
+
19
+ """
20
+
7
21
  # s3 configuration
8
22
  s3_endpoint_url: str | None = None
9
23
  s3_access_key: str | None = None
@@ -15,5 +29,13 @@ class ObjectStorageServiceConfig(BaseModel):
15
29
 
16
30
 
17
31
  class TransferPath(BaseModel):
32
+ """Transfer path information with backend type.
33
+
34
+ Attributes:
35
+ backend: Storage backend type (s3, abfs, or local).
36
+ path: Full path to the file or object.
37
+
38
+ """
39
+
18
40
  backend: Literal["s3", "abfs", "local"]
19
41
  path: str