data-sourcerer 0.1.1__py3-none-any.whl → 0.2.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 (54) hide show
  1. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/METADATA +24 -4
  2. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/RECORD +53 -50
  3. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/WHEEL +1 -1
  4. sourcerer/__init__.py +1 -1
  5. sourcerer/domain/access_credentials/entities.py +17 -0
  6. sourcerer/domain/access_credentials/exceptions.py +1 -1
  7. sourcerer/domain/access_credentials/repositories.py +1 -1
  8. sourcerer/domain/access_credentials/services.py +14 -2
  9. sourcerer/domain/file_system/exceptions.py +1 -1
  10. sourcerer/domain/file_system/services.py +2 -2
  11. sourcerer/domain/shared/entities.py +1 -0
  12. sourcerer/domain/storage_provider/entities.py +3 -4
  13. sourcerer/domain/storage_provider/exceptions.py +1 -1
  14. sourcerer/domain/storage_provider/services.py +13 -9
  15. sourcerer/infrastructure/access_credentials/exceptions.py +15 -2
  16. sourcerer/infrastructure/access_credentials/registry.py +3 -4
  17. sourcerer/infrastructure/access_credentials/services.py +141 -44
  18. sourcerer/infrastructure/db/models.py +1 -1
  19. sourcerer/infrastructure/file_system/exceptions.py +9 -9
  20. sourcerer/infrastructure/file_system/services.py +16 -16
  21. sourcerer/infrastructure/storage_provider/exceptions.py +28 -8
  22. sourcerer/infrastructure/storage_provider/registry.py +2 -3
  23. sourcerer/infrastructure/storage_provider/services/__init__.py +0 -0
  24. sourcerer/infrastructure/storage_provider/services/azure.py +261 -0
  25. sourcerer/infrastructure/storage_provider/services/gcp.py +277 -0
  26. sourcerer/infrastructure/storage_provider/services/s3.py +290 -0
  27. sourcerer/infrastructure/utils.py +2 -4
  28. sourcerer/presentation/screens/critical_error/main.py +3 -4
  29. sourcerer/presentation/screens/file_system_finder/main.py +4 -4
  30. sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +12 -12
  31. sourcerer/presentation/screens/main/main.py +57 -33
  32. sourcerer/presentation/screens/main/messages/delete_request.py +1 -2
  33. sourcerer/presentation/screens/main/messages/download_request.py +1 -2
  34. sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +3 -3
  35. sourcerer/presentation/screens/main/widgets/gradient.py +2 -5
  36. sourcerer/presentation/screens/main/widgets/resizing_rule.py +1 -1
  37. sourcerer/presentation/screens/main/widgets/storage_content.py +12 -13
  38. sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +8 -6
  39. sourcerer/presentation/screens/preview_content/main.py +15 -4
  40. sourcerer/presentation/screens/preview_content/styles.tcss +2 -1
  41. sourcerer/presentation/screens/provider_creds_list/main.py +2 -2
  42. sourcerer/presentation/screens/provider_creds_registration/main.py +26 -11
  43. sourcerer/presentation/screens/question/main.py +1 -1
  44. sourcerer/presentation/screens/shared/containers.py +1 -1
  45. sourcerer/presentation/screens/shared/widgets/labeled_input.py +1 -1
  46. sourcerer/presentation/screens/storage_action_progress/main.py +34 -20
  47. sourcerer/presentation/screens/storage_action_progress/styles.tcss +11 -0
  48. sourcerer/presentation/utils.py +7 -3
  49. sourcerer/settings.py +4 -0
  50. sourcerer/utils.py +2 -2
  51. sourcerer/infrastructure/storage_provider/services.py +0 -509
  52. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/entry_points.txt +0 -0
  53. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/licenses/LICENSE +0 -0
  54. {data_sourcerer-0.1.1.dist-info → data_sourcerer-0.2.0.dist-info}/top_level.txt +0 -0
@@ -9,20 +9,28 @@ import json
9
9
  from abc import ABC
10
10
 
11
11
  import boto3
12
+ from azure.identity import ClientSecretCredential
12
13
  from dependency_injector.wiring import Provide
13
14
  from google.cloud import storage
14
15
 
15
- from sourcerer.domain.access_credentials.entities import Credentials, Boto3Credentials
16
+ from sourcerer.domain.access_credentials.entities import (
17
+ AzureCredentials,
18
+ Boto3Credentials,
19
+ Credentials,
20
+ )
16
21
  from sourcerer.domain.access_credentials.repositories import BaseCredentialsRepository
17
22
  from sourcerer.domain.access_credentials.services import (
18
- BaseAccessCredentialsService,
19
23
  AuthField,
24
+ BaseAccessCredentialsService,
20
25
  )
21
26
  from sourcerer.domain.shared.entities import StorageProvider
22
- from sourcerer.infrastructure.access_credentials.exceptions import CredentialsAuthError
27
+ from sourcerer.infrastructure.access_credentials.exceptions import (
28
+ CredentialsAuthError,
29
+ MissingAuthFieldsError,
30
+ )
23
31
  from sourcerer.infrastructure.access_credentials.registry import (
24
- access_credentials_method,
25
32
  AccessCredentialsMethod,
33
+ access_credentials_method,
26
34
  )
27
35
  from sourcerer.infrastructure.utils import generate_uuid
28
36
  from sourcerer.presentation.di_container import DiContainer
@@ -116,6 +124,22 @@ class AccessCredentialsService(BaseAccessCredentialsService, ABC):
116
124
  """
117
125
  super().__init__(credentials_repo)
118
126
 
127
+ @classmethod
128
+ def validate_auth_fields_values(cls, auth_fields: dict) -> None:
129
+ """
130
+ Validate authentication fields.
131
+
132
+ Args:
133
+ auth_fields (dict): Dictionary containing authentication field,
134
+ where keys are field names and values are field values
135
+
136
+ Raises:
137
+ MissingAuthFieldsError: If any required authentication fields are missing
138
+ """
139
+ for field in cls.auth_fields():
140
+ if field.required and field.key not in auth_fields:
141
+ raise MissingAuthFieldsError(f"Missing required field: {field.key}")
142
+
119
143
 
120
144
  class S3AccessCredentialsService(AccessCredentialsService, ABC):
121
145
  """
@@ -176,23 +200,26 @@ class S3AccessKeySecretKeyPair(S3AccessCredentialsService):
176
200
  Returns:
177
201
  boto3.Session: Authenticated boto3 session
178
202
  """
179
- credentials: dict = json.loads(credentials)
203
+ try:
204
+ credentials: dict = json.loads(credentials)
180
205
 
181
- session_args = {
182
- "aws_access_key_id": credentials.get("aws_access_key_id"),
183
- "aws_secret_access_key": credentials.get("aws_secret_access_key"),
184
- }
206
+ session_args = {
207
+ "aws_access_key_id": credentials.get("aws_access_key_id"),
208
+ "aws_secret_access_key": credentials.get("aws_secret_access_key"),
209
+ }
185
210
 
186
- if region := credentials.get("region"):
187
- session_args["region_name"] = region
211
+ if region := credentials.get("region"):
212
+ session_args["region_name"] = region
188
213
 
189
- session = boto3.Session(**session_args)
214
+ session = boto3.Session(**session_args)
190
215
 
191
- return Boto3Credentials(
192
- session=session,
193
- endpoint_url=credentials.get("endpoint_url", None),
194
- signature_version=credentials.get("signature_version", None),
195
- )
216
+ return Boto3Credentials(
217
+ session=session,
218
+ endpoint_url=credentials.get("endpoint_url", None),
219
+ signature_version=credentials.get("signature_version", None),
220
+ )
221
+ except Exception as e:
222
+ raise CredentialsAuthError("Failed to authenticate") from e
196
223
 
197
224
  @classmethod
198
225
  def auth_fields(cls):
@@ -260,15 +287,18 @@ class S3ProfileName(S3AccessCredentialsService):
260
287
  Returns:
261
288
  boto3.Session: Authenticated boto3 session
262
289
  """
263
- credentials: dict = json.loads(credentials)
264
- session = boto3.Session(profile_name=credentials.get("profile_name"))
265
- return Boto3Credentials(
266
- session=session,
267
- endpoint_url=credentials.get("endpoint_url"),
268
- )
290
+ try:
291
+ credentials: dict = json.loads(credentials)
292
+ session = boto3.Session(profile_name=credentials.get("profile_name"))
293
+ return Boto3Credentials(
294
+ session=session,
295
+ endpoint_url=credentials.get("endpoint_url"),
296
+ )
297
+ except Exception as e:
298
+ raise CredentialsAuthError("Failed to authenticate") from e
269
299
 
270
300
  @classmethod
271
- def auth_fields(cls):
301
+ def auth_fields(cls) -> list[AuthField]:
272
302
  """
273
303
  Get list of authentication fields.
274
304
 
@@ -286,15 +316,15 @@ class S3ProfileName(S3AccessCredentialsService):
286
316
  )
287
317
  class GCPCredentialsService(AccessCredentialsService):
288
318
  """
289
- Google Cloud Platform test credentials service.
319
+ Google Cloud Platform json credentials service.
290
320
 
291
321
  This class provides methods for storing, retrieving, and authenticating
292
- with GCP using test credentials.
322
+ with GCP using credentials.
293
323
  """
294
324
 
295
325
  def store(self, name, credentials: dict):
296
326
  """
297
- Store GCP test credentials.
327
+ Store GCP json credentials.
298
328
 
299
329
  Args:
300
330
  name (str): Name identifier for the credentials
@@ -357,22 +387,6 @@ class GCPCredentialsService(AccessCredentialsService):
357
387
  except json.JSONDecodeError as e:
358
388
  raise ValueError("Invalid service account JSON format") from e
359
389
 
360
- # Validate required fields for service account
361
- required_fields = [
362
- "type",
363
- "project_id",
364
- "private_key_id",
365
- "private_key",
366
- "client_email",
367
- ]
368
- missing_fields = [
369
- field for field in required_fields if field not in service_acc_info
370
- ]
371
- if missing_fields:
372
- raise ValueError(
373
- f"Service account missing required fields: {', '.join(missing_fields)}"
374
- )
375
-
376
390
  # Create and return the authenticated client
377
391
  return storage.Client.from_service_account_info(service_acc_info)
378
392
 
@@ -384,7 +398,7 @@ class GCPCredentialsService(AccessCredentialsService):
384
398
  ) from e
385
399
 
386
400
  @classmethod
387
- def auth_fields(cls):
401
+ def auth_fields(cls) -> list[AuthField]:
388
402
  """
389
403
  Get list of authentication fields.
390
404
 
@@ -394,3 +408,86 @@ class GCPCredentialsService(AccessCredentialsService):
394
408
  return [
395
409
  AuthField("service_acc", "Service acc", True, True),
396
410
  ]
411
+
412
+
413
+ @access_credentials_method(
414
+ AccessCredentialsMethod(StorageProvider.AzureStorage, "Client Secret Credentials")
415
+ )
416
+ class AzureClientSecretCredentialsService(AccessCredentialsService):
417
+
418
+ def store(self, name, credentials: dict):
419
+ """
420
+ Store Azure client_id and secret_key_pair credentials.
421
+
422
+ Args:
423
+ name (str): Name identifier for the credentials
424
+ credentials (dict): Dictionary containing Azure application credential information
425
+ """
426
+ self.credentials_repo.create(
427
+ Credentials(
428
+ uuid=generate_uuid(),
429
+ name=name,
430
+ provider=StorageProvider.AzureStorage,
431
+ credentials_type="Client Secret Credentials",
432
+ credentials=json.dumps(credentials),
433
+ active=True,
434
+ )
435
+ )
436
+
437
+ def extract(self, uuid):
438
+ """
439
+ Extract credentials by UUID.
440
+
441
+ Args:
442
+ uuid (str): UUID of the credentials to extract
443
+
444
+ Returns:
445
+ Credentials: The credentials object
446
+ """
447
+ return self.credentials_repo.get(uuid)
448
+
449
+ def authenticate(self, credentials: str): # type: ignore
450
+ try:
451
+ # Parse the outer JSON structure
452
+ parsed_credentials = json.loads(credentials)
453
+ subscription_id = parsed_credentials.get("subscription_id")
454
+ cloud_suffix = (
455
+ parsed_credentials.get("cloud_suffix") or "blob.core.windows.net"
456
+ )
457
+
458
+ client_credentials = ClientSecretCredential(
459
+ tenant_id=parsed_credentials.get("tenant_id"),
460
+ client_id=parsed_credentials.get("client_id"),
461
+ client_secret=parsed_credentials.get("client_secret"),
462
+ )
463
+
464
+ return AzureCredentials(
465
+ credentials=client_credentials,
466
+ subscription_id=subscription_id,
467
+ cloud_suffix=cloud_suffix,
468
+ )
469
+
470
+ except json.JSONDecodeError as e:
471
+ raise CredentialsAuthError(f"Invalid credentials format: {str(e)}") from e
472
+ except Exception as e:
473
+ raise CredentialsAuthError(
474
+ f"Failed to authenticate with Azure: {str(e)}"
475
+ ) from e
476
+
477
+ @classmethod
478
+ def auth_fields(cls) -> list[AuthField]:
479
+ """
480
+ Get list of authentication fields.
481
+
482
+ Returns:
483
+ List[AuthField]: List of authentication field definitions
484
+ """
485
+ return [
486
+ AuthField("subscription_id", "Subscription Id", True),
487
+ AuthField("tenant_id", "Tenant Id", True),
488
+ AuthField("client_id", "Client Id", True),
489
+ AuthField("client_secret", "Client Secret", True),
490
+ AuthField(
491
+ "cloud_suffix", "Cloud Suffix (default blob.core.windows.net)", False
492
+ ),
493
+ ]
@@ -7,7 +7,7 @@ and their relationships.
7
7
 
8
8
  from datetime import datetime
9
9
 
10
- from sqlalchemy import Column, Integer, Boolean, DateTime, String
10
+ from sqlalchemy import Boolean, Column, DateTime, Integer, String
11
11
  from sqlalchemy.orm import declarative_base
12
12
  from sqlalchemy_utils.types.encrypted.encrypted_type import EncryptedType
13
13
 
@@ -5,10 +5,10 @@ This module defines exception classes for handling errors that occur
5
5
  during interactions with the local file system.
6
6
  """
7
7
 
8
- from sourcerer.domain.file_system.exceptions import BaseFileSystemException
8
+ from sourcerer.domain.file_system.exceptions import BaseFileSystemError
9
9
 
10
10
 
11
- class FileSystemGrepException(BaseFileSystemException):
11
+ class FileSystemGrepError(BaseFileSystemError):
12
12
  """
13
13
  Custom exception for errors occurring during a file system search operation.
14
14
 
@@ -18,7 +18,7 @@ class FileSystemGrepException(BaseFileSystemException):
18
18
  """
19
19
 
20
20
 
21
- class ReadFileException(BaseFileSystemException):
21
+ class ReadFileError(BaseFileSystemError):
22
22
  """
23
23
  Custom exception for errors occurring during a file system read operation.
24
24
 
@@ -28,7 +28,7 @@ class ReadFileException(BaseFileSystemException):
28
28
  """
29
29
 
30
30
 
31
- class CreateFileException(BaseFileSystemException):
31
+ class CreateFileError(BaseFileSystemError):
32
32
  """
33
33
  Custom exception for errors occurring during a file system create file operation.
34
34
 
@@ -38,7 +38,7 @@ class CreateFileException(BaseFileSystemException):
38
38
  """
39
39
 
40
40
 
41
- class CreateDirException(BaseFileSystemException):
41
+ class CreateDirError(BaseFileSystemError):
42
42
  """
43
43
  Custom exception for errors occurring during a file system create directory operation.
44
44
 
@@ -48,7 +48,7 @@ class CreateDirException(BaseFileSystemException):
48
48
  """
49
49
 
50
50
 
51
- class MoveFileException(BaseFileSystemException):
51
+ class MoveFileError(BaseFileSystemError):
52
52
  """
53
53
  Exception raised for errors during file move operation.
54
54
 
@@ -59,7 +59,7 @@ class MoveFileException(BaseFileSystemException):
59
59
  """
60
60
 
61
61
 
62
- class ListDirException(BaseFileSystemException):
62
+ class ListDirError(BaseFileSystemError):
63
63
  """
64
64
  Custom exception for errors occurring during a file system list directory operation.
65
65
 
@@ -69,7 +69,7 @@ class ListDirException(BaseFileSystemException):
69
69
  """
70
70
 
71
71
 
72
- class DeleteFileException(BaseFileSystemException):
72
+ class DeleteFileError(BaseFileSystemError):
73
73
  """
74
74
  Custom exception for errors occurring during a file system delete file operation.
75
75
 
@@ -79,7 +79,7 @@ class DeleteFileException(BaseFileSystemException):
79
79
  """
80
80
 
81
81
 
82
- class DeleteDirException(BaseFileSystemException):
82
+ class DeleteDirError(BaseFileSystemError):
83
83
  """
84
84
  Custom exception for errors occurring during a file system delete directory operation.
85
85
 
@@ -10,8 +10,8 @@ from pathlib import Path
10
10
  from sourcerer.domain.file_system.entities import ListDirOutput
11
11
  from sourcerer.domain.file_system.services import BaseFileSystemService
12
12
  from sourcerer.infrastructure.file_system.exceptions import (
13
- ReadFileException,
14
- ListDirException,
13
+ ListDirError,
14
+ ReadFileError,
15
15
  )
16
16
  from sourcerer.infrastructure.utils import custom_sort_key
17
17
 
@@ -56,18 +56,18 @@ class FileSystemService(BaseFileSystemService):
56
56
  str: The processed data extracted from the file.
57
57
 
58
58
  Raises:
59
- ReadFileException: An error occurred during the file read operation.
59
+ ReadFileError: An error occurred during the file read operation.
60
60
  """
61
61
 
62
62
  if not isinstance(path, Path):
63
- raise ReadFileException("file_path must be a Path object")
63
+ raise ReadFileError("file_path must be a Path object")
64
64
  if not path.exists():
65
- raise ReadFileException(f"File not found: {path}")
66
- self.validate_path_within_work_dir(path, ReadFileException)
65
+ raise ReadFileError(f"File not found: {path}")
66
+ self.validate_path_within_work_dir(path, ReadFileError)
67
67
  try:
68
68
  return path.read_text()
69
69
  except Exception as e:
70
- raise ReadFileException(f"Error reading file: {e}") from e
70
+ raise ReadFileError(f"Error reading file: {e}") from e
71
71
 
72
72
  def list_dir(
73
73
  self,
@@ -91,19 +91,19 @@ class FileSystemService(BaseFileSystemService):
91
91
  - directories (list[Path]): Sorted list of directory paths
92
92
 
93
93
  Raises:
94
- ListDirException: If the path is invalid, directory doesn't exist, or path is not a directory.
94
+ ListDirError: If the path is invalid, directory doesn't exist, or path is not a directory.
95
95
 
96
96
  """
97
97
  if not isinstance(path, Path):
98
- raise ListDirException("Path must be a Path object")
98
+ raise ListDirError("Path must be a Path object")
99
99
  if not path.exists():
100
- raise ListDirException(f"Directory not found: {path}")
100
+ raise ListDirError(f"Directory not found: {path}")
101
101
  if not path.is_dir():
102
- raise ListDirException(f"Path is not a directory: {path}")
102
+ raise ListDirError(f"Path is not a directory: {path}")
103
103
  try:
104
104
  path.relative_to(self.work_dir)
105
105
  except ValueError as e:
106
- raise ListDirException("Access denied: Path outside work directory") from e
106
+ raise ListDirError("Access denied: Path outside work directory") from e
107
107
  try:
108
108
  files = []
109
109
  directories = []
@@ -112,10 +112,10 @@ class FileSystemService(BaseFileSystemService):
112
112
  items = 0
113
113
  for entry in entries:
114
114
  if max_items and items > max_items:
115
- raise ListDirException(
115
+ raise ListDirError(
116
116
  f"Too many items, max processable dir size: {max_items}"
117
117
  )
118
- items += 1
118
+ items += 1 # noqa SIM103
119
119
  target = files if entry.is_file() else directories
120
120
  target.append(entry.relative_to(path) if relative_paths else entry)
121
121
  return ListDirOutput(
@@ -123,9 +123,9 @@ class FileSystemService(BaseFileSystemService):
123
123
  directories=sorted(directories, key=custom_sort_key),
124
124
  )
125
125
  except PermissionError as e:
126
- raise ListDirException(f"Access denied: {e}") from e
126
+ raise ListDirError(f"Access denied: {e}") from e
127
127
  except Exception as e:
128
- raise ListDirException(f"Error listing directory: {e}") from e
128
+ raise ListDirError(f"Error listing directory: {e}") from e
129
129
 
130
130
  def validate_path_within_work_dir(self, path: Path, exception_class: type) -> None:
131
131
  """
@@ -5,10 +5,10 @@ This module defines exception classes for handling errors that occur
5
5
  during interactions with cloud storage providers.
6
6
  """
7
7
 
8
- from sourcerer.domain.storage_provider.exceptions import BaseStorageProviderException
8
+ from sourcerer.domain.storage_provider.exceptions import BaseStorageProviderError
9
9
 
10
10
 
11
- class CredentialsNotFoundException(BaseStorageProviderException):
11
+ class CredentialsNotFoundError(BaseStorageProviderError):
12
12
  """
13
13
  Exception raised when credentials are not found for a cloud storage provider.
14
14
 
@@ -18,7 +18,7 @@ class CredentialsNotFoundException(BaseStorageProviderException):
18
18
  """
19
19
 
20
20
 
21
- class ListStoragesException(BaseStorageProviderException):
21
+ class ListStoragesError(BaseStorageProviderError):
22
22
  """
23
23
  Exception raised for errors that occur during the listing of cloud storage services.
24
24
 
@@ -28,7 +28,7 @@ class ListStoragesException(BaseStorageProviderException):
28
28
  """
29
29
 
30
30
 
31
- class StoragePermissionException(BaseStorageProviderException):
31
+ class StoragePermissionError(BaseStorageProviderError):
32
32
  """
33
33
  Exception raised for permission-related errors in cloud storage operations.
34
34
 
@@ -37,7 +37,7 @@ class StoragePermissionException(BaseStorageProviderException):
37
37
  """
38
38
 
39
39
 
40
- class ListStorageItemsException(BaseStorageProviderException):
40
+ class ListStorageItemsError(BaseStorageProviderError):
41
41
  """
42
42
  Exception raised when an error occurs while listing items in cloud storage.
43
43
 
@@ -47,7 +47,17 @@ class ListStorageItemsException(BaseStorageProviderException):
47
47
  """
48
48
 
49
49
 
50
- class ReadStorageItemsException(BaseStorageProviderException):
50
+ class BlobNotFoundError(BaseStorageProviderError):
51
+ """
52
+ Exception raised when a blob (file) is not found in cloud storage.
53
+
54
+ This exception is a specific case of BaseStorageProviderException
55
+ and should be used to indicate that the requested blob could not
56
+ be located in the specified cloud storage service.
57
+ """
58
+
59
+
60
+ class ReadStorageItemsError(BaseStorageProviderError):
51
61
  """
52
62
  Exception raised for errors encountered while reading items from cloud storage.
53
63
 
@@ -57,7 +67,7 @@ class ReadStorageItemsException(BaseStorageProviderException):
57
67
  """
58
68
 
59
69
 
60
- class DeleteStorageItemsException(BaseStorageProviderException):
70
+ class DeleteStorageItemsError(BaseStorageProviderError):
61
71
  """
62
72
  Exception raised for errors that occur during the deletion of storage items.
63
73
 
@@ -67,7 +77,7 @@ class DeleteStorageItemsException(BaseStorageProviderException):
67
77
  """
68
78
 
69
79
 
70
- class UploadStorageItemsException(BaseStorageProviderException):
80
+ class UploadStorageItemsError(BaseStorageProviderError):
71
81
  """
72
82
  Exception raised for errors that occur during the upload of items
73
83
  to cloud storage.
@@ -76,3 +86,13 @@ class UploadStorageItemsException(BaseStorageProviderException):
76
86
  and is used to signal issues encountered while uploading data to
77
87
  cloud storage services.
78
88
  """
89
+
90
+
91
+ class AzureMissingContainerError(BaseStorageProviderError):
92
+ """
93
+ Exception raised when a container is not found in Azure cloud storage.
94
+
95
+ This exception is a specific case of BaseStorageProviderException
96
+ and should be used to indicate that the requested container could not
97
+ be located in the specified Azure cloud storage service.
98
+ """
@@ -6,7 +6,6 @@ storage provider services.
6
6
  """
7
7
 
8
8
  import functools
9
- from typing import Type, Dict
10
9
 
11
10
  from sourcerer.domain.storage_provider.services import BaseStorageProviderService
12
11
  from sourcerer.infrastructure.utils import Singleton
@@ -23,9 +22,9 @@ class StorageProviderRegistry(metaclass=Singleton):
23
22
  def __init__(self, *args, **kwargs):
24
23
  """Initialize the registry with an empty dictionary."""
25
24
  super().__init__(*args, **kwargs)
26
- self._items_: Dict[str, type] = {}
25
+ self._items_: dict[str, type] = {}
27
26
 
28
- def register(self, storage_provider: str, cls: Type[BaseStorageProviderService]):
27
+ def register(self, storage_provider: str, cls: type[BaseStorageProviderService]):
29
28
  """
30
29
  Register a storage provider service implementation.
31
30