cloudtoolbox 0.1.0__tar.gz → 0.3.0__tar.gz

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 (30) hide show
  1. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/LICENSE +1 -1
  2. cloudtoolbox-0.3.0/PKG-INFO +86 -0
  3. cloudtoolbox-0.3.0/README.md +55 -0
  4. cloudtoolbox-0.3.0/cloud/amazon/s3.py +40 -0
  5. cloudtoolbox-0.3.0/cloud/amazon/sns.py +11 -0
  6. cloudtoolbox-0.3.0/cloud/amazon/sqs.py +11 -0
  7. cloudtoolbox-0.3.0/cloud/factory.py +53 -0
  8. cloudtoolbox-0.3.0/cloud/google/storage.py +42 -0
  9. cloudtoolbox-0.3.0/cloud/protocols.py +28 -0
  10. cloudtoolbox-0.3.0/cloudtoolbox.egg-info/PKG-INFO +86 -0
  11. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloudtoolbox.egg-info/SOURCES.txt +2 -0
  12. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloudtoolbox.egg-info/requires.txt +5 -0
  13. cloudtoolbox-0.3.0/pyproject.toml +53 -0
  14. cloudtoolbox-0.3.0/tests/test_factory.py +126 -0
  15. cloudtoolbox-0.1.0/PKG-INFO +0 -19
  16. cloudtoolbox-0.1.0/README.md +0 -3
  17. cloudtoolbox-0.1.0/cloud/amazon/s3.py +0 -11
  18. cloudtoolbox-0.1.0/cloud/factory.py +0 -29
  19. cloudtoolbox-0.1.0/cloud/google/storage.py +0 -13
  20. cloudtoolbox-0.1.0/cloud/protocols.py +0 -11
  21. cloudtoolbox-0.1.0/cloudtoolbox.egg-info/PKG-INFO +0 -19
  22. cloudtoolbox-0.1.0/pyproject.toml +0 -45
  23. cloudtoolbox-0.1.0/tests/test_factory.py +0 -57
  24. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloud/__init__.py +0 -0
  25. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloud/amazon/__init__.py +0 -0
  26. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloud/google/__init__.py +0 -0
  27. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloud/google/pubsub.py +0 -0
  28. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloudtoolbox.egg-info/dependency_links.txt +0 -0
  29. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/cloudtoolbox.egg-info/top_level.txt +0 -0
  30. {cloudtoolbox-0.1.0 → cloudtoolbox-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Noverde
3
+ Copyright (c) 2024 Dotz
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: cloudtoolbox
3
+ Version: 0.3.0
4
+ Summary: Toolbox for the cloud.
5
+ Author-email: Dotz Developers <devs-dotz@dotz.com>
6
+ Project-URL: Repository, https://github.com/DotzInc/cloud-toolbox
7
+ Keywords: cloud,tools,utilities,google,gcp,amazon,aws
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Internet
16
+ Classifier: Topic :: Software Development
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: amazon
23
+ Requires-Dist: boto3; extra == "amazon"
24
+ Provides-Extra: google
25
+ Requires-Dist: google-cloud-pubsub; extra == "google"
26
+ Requires-Dist: google-cloud-storage; extra == "google"
27
+ Provides-Extra: all
28
+ Requires-Dist: boto3; extra == "all"
29
+ Requires-Dist: google-cloud-pubsub; extra == "all"
30
+ Requires-Dist: google-cloud-storage; extra == "all"
31
+
32
+ # Cloud Toolbox
33
+
34
+ ![Tests](https://github.com/DotzInc/cloud-toolbox/actions/workflows/tests.yml/badge.svg?event=push)
35
+ ![PyPI - Version](https://img.shields.io/pypi/v/cloudtoolbox)
36
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cloudtoolbox)
37
+
38
+ Decouple your applications from cloud providers with carefully crafted service interfaces.
39
+
40
+ ## Requirements
41
+
42
+ * Python 3.8+
43
+
44
+ ## Installation
45
+
46
+ To install Cloud Toolbox, use pip:
47
+
48
+ ```sh
49
+ pip install cloudtoolbox
50
+ ```
51
+
52
+ ### Extras
53
+
54
+ Cloud Toolbox offers the following optional dependencies for easy installation of provider SDKs:
55
+
56
+ * `cloudtoolbox[amazon]` - Installs the Amazon AWS SDK.
57
+ * `cloudtoolbox[google]` - Installs the Google Cloud SDK.
58
+ * `cloudtoolbox[all]` - Installs SDKs for both providers.
59
+
60
+ ## Example
61
+
62
+ Uploading a file to Google Cloud Storage.
63
+
64
+ ```python
65
+ from cloud import factory
66
+ from cloud.google.storage import Uploader
67
+
68
+ FileUploader = factory.storage_uploader(Uploader)
69
+
70
+ bucket = "my-bucket"
71
+ filename = "notes.txt"
72
+ filepath = f"/path/to/{filename}"
73
+
74
+ uploader = FileUploader()
75
+ uploader.upload(bucket, filename, filepath)
76
+ ```
77
+
78
+ Switching from Cloud Storage to Amazon S3.
79
+
80
+ ```python
81
+ # Replace this import
82
+ from cloud.google.storage import Uploader
83
+
84
+ # For this one
85
+ from cloud.amazon.s3 import Uploader
86
+ ```
@@ -0,0 +1,55 @@
1
+ # Cloud Toolbox
2
+
3
+ ![Tests](https://github.com/DotzInc/cloud-toolbox/actions/workflows/tests.yml/badge.svg?event=push)
4
+ ![PyPI - Version](https://img.shields.io/pypi/v/cloudtoolbox)
5
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cloudtoolbox)
6
+
7
+ Decouple your applications from cloud providers with carefully crafted service interfaces.
8
+
9
+ ## Requirements
10
+
11
+ * Python 3.8+
12
+
13
+ ## Installation
14
+
15
+ To install Cloud Toolbox, use pip:
16
+
17
+ ```sh
18
+ pip install cloudtoolbox
19
+ ```
20
+
21
+ ### Extras
22
+
23
+ Cloud Toolbox offers the following optional dependencies for easy installation of provider SDKs:
24
+
25
+ * `cloudtoolbox[amazon]` - Installs the Amazon AWS SDK.
26
+ * `cloudtoolbox[google]` - Installs the Google Cloud SDK.
27
+ * `cloudtoolbox[all]` - Installs SDKs for both providers.
28
+
29
+ ## Example
30
+
31
+ Uploading a file to Google Cloud Storage.
32
+
33
+ ```python
34
+ from cloud import factory
35
+ from cloud.google.storage import Uploader
36
+
37
+ FileUploader = factory.storage_uploader(Uploader)
38
+
39
+ bucket = "my-bucket"
40
+ filename = "notes.txt"
41
+ filepath = f"/path/to/{filename}"
42
+
43
+ uploader = FileUploader()
44
+ uploader.upload(bucket, filename, filepath)
45
+ ```
46
+
47
+ Switching from Cloud Storage to Amazon S3.
48
+
49
+ ```python
50
+ # Replace this import
51
+ from cloud.google.storage import Uploader
52
+
53
+ # For this one
54
+ from cloud.amazon.s3 import Uploader
55
+ ```
@@ -0,0 +1,40 @@
1
+ from typing import Any
2
+
3
+ import boto3
4
+
5
+
6
+ class Client:
7
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
8
+ self.client = boto3.client("s3", *args, **kwargs)
9
+
10
+ def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None:
11
+ self.client.upload_file(source_filename, bucket_name, destination_filename)
12
+
13
+ def download(self, bucket_name: str, source_filename: str, destination_filename: str) -> None:
14
+ self.client.download_file(bucket_name, source_filename, destination_filename)
15
+
16
+ def generate_presigned_url(
17
+ self,
18
+ bucket_name: str,
19
+ source_filename: str,
20
+ expiration: int,
21
+ ) -> str:
22
+ url = self.client.generate_presigned_url(
23
+ ClientMethod="get_object",
24
+ Params={"Bucket": bucket_name, "Key": source_filename},
25
+ ExpiresIn=expiration,
26
+ )
27
+
28
+ return url
29
+
30
+
31
+ class Uploader(Client):
32
+ pass
33
+
34
+
35
+ class Downloader(Client):
36
+ pass
37
+
38
+
39
+ class URLSigner(Client):
40
+ pass
@@ -0,0 +1,11 @@
1
+ from typing import Any
2
+
3
+ import boto3
4
+
5
+
6
+ class Publisher:
7
+ def __init__(self, *args: Any, **kwargs: Any):
8
+ self.client = boto3.client("sns", *args, **kwargs)
9
+
10
+ def publish(self, recipient: str, message: str, **attrs: Any) -> None:
11
+ self.client.publish(TargetArn=recipient, Message=message, **attrs)
@@ -0,0 +1,11 @@
1
+ from typing import Any
2
+
3
+ import boto3
4
+
5
+
6
+ class Publisher:
7
+ def __init__(self, *args: Any, **kwargs: Any):
8
+ self.client = boto3.client("sqs", *args, **kwargs)
9
+
10
+ def publish(self, recipient: str, message: str, **attrs: Any) -> None:
11
+ self.client.send_message(QueueUrl=recipient, MessageBody=message, **attrs)
@@ -0,0 +1,53 @@
1
+ from typing import Any, Callable, Type
2
+
3
+ from cloud.protocols import MessagePublisher, StorageDownloader, StorageUploader, StorageURLSigner
4
+
5
+ error_message = "%(class)s does not implement %(protocol)s protocol"
6
+
7
+
8
+ def storage_uploader(
9
+ uploader_class: Type[StorageUploader], *args: Any, **kwargs: Any
10
+ ) -> Callable[[], StorageUploader]:
11
+ error = error_message % {"class": uploader_class.__name__, "protocol": "StorageUploader"}
12
+ assert issubclass(uploader_class, StorageUploader), error
13
+
14
+ def maker() -> StorageUploader:
15
+ return uploader_class(*args, **kwargs)
16
+
17
+ return maker
18
+
19
+
20
+ def storage_downloader(
21
+ downloader_class: Type[StorageDownloader], *args: Any, **kwargs: Any
22
+ ) -> Callable[[], StorageDownloader]:
23
+ error = error_message % {"class": downloader_class.__name__, "protocol": "StorageDownloader"}
24
+ assert issubclass(downloader_class, StorageDownloader), error
25
+
26
+ def maker() -> StorageDownloader:
27
+ return downloader_class(*args, **kwargs)
28
+
29
+ return maker
30
+
31
+
32
+ def storage_urlsigner(
33
+ urlsigner_class: Type[StorageURLSigner], *args: Any, **kwargs: Any
34
+ ) -> Callable[[], StorageURLSigner]:
35
+ error = error_message % {"class": urlsigner_class.__name__, "protocol": "StorageURLSigner"}
36
+ assert issubclass(urlsigner_class, StorageURLSigner), error
37
+
38
+ def maker() -> StorageURLSigner:
39
+ return urlsigner_class(*args, **kwargs)
40
+
41
+ return maker
42
+
43
+
44
+ def message_publisher(
45
+ publisher_class: Type[MessagePublisher], *args: Any, **kwargs: Any
46
+ ) -> Callable[[], MessagePublisher]:
47
+ error = error_message % {"class": publisher_class.__name__, "protocol": "MessagePublisher"}
48
+ assert issubclass(publisher_class, MessagePublisher), error
49
+
50
+ def maker() -> MessagePublisher:
51
+ return publisher_class(*args, **kwargs)
52
+
53
+ return maker
@@ -0,0 +1,42 @@
1
+ import datetime
2
+ from typing import Any
3
+
4
+ from google.cloud import storage
5
+
6
+
7
+ class Client:
8
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
9
+ self.client = storage.Client(*args, **kwargs)
10
+
11
+ def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None:
12
+ blob = self.client.bucket(bucket_name).blob(destination_filename)
13
+ blob.upload_from_filename(source_filename)
14
+
15
+ def download(self, bucket_name: str, source_filename: str, destination_filename: str) -> None:
16
+ blob = self.client.bucket(bucket_name).blob(source_filename)
17
+ blob.download_to_filename(destination_filename)
18
+
19
+ def generate_presigned_url(
20
+ self,
21
+ bucket_name: str,
22
+ source_filename: str,
23
+ expiration: int,
24
+ ) -> str:
25
+ blob = self.client.bucket(bucket_name).blob(source_filename)
26
+ url = blob.generate_signed_url(
27
+ expiration=datetime.timedelta(seconds=expiration),
28
+ version="v4",
29
+ )
30
+ return url
31
+
32
+
33
+ class Uploader(Client):
34
+ pass
35
+
36
+
37
+ class Downloader(Client):
38
+ pass
39
+
40
+
41
+ class URLSigner(Client):
42
+ pass
@@ -0,0 +1,28 @@
1
+ from typing import Any, Protocol, runtime_checkable
2
+
3
+
4
+ @runtime_checkable
5
+ class StorageUploader(Protocol):
6
+ def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None: ...
7
+
8
+
9
+ @runtime_checkable
10
+ class StorageDownloader(Protocol):
11
+ def download(
12
+ self, bucket_name: str, source_filename: str, destination_filename: str
13
+ ) -> None: ...
14
+
15
+
16
+ @runtime_checkable
17
+ class StorageURLSigner(Protocol):
18
+ def generate_presigned_url(
19
+ self,
20
+ bucket_name: str,
21
+ source_filename: str,
22
+ expiration: int,
23
+ ) -> str: ...
24
+
25
+
26
+ @runtime_checkable
27
+ class MessagePublisher(Protocol):
28
+ def publish(self, recipient: str, message: str, **attrs: Any) -> None: ...
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: cloudtoolbox
3
+ Version: 0.3.0
4
+ Summary: Toolbox for the cloud.
5
+ Author-email: Dotz Developers <devs-dotz@dotz.com>
6
+ Project-URL: Repository, https://github.com/DotzInc/cloud-toolbox
7
+ Keywords: cloud,tools,utilities,google,gcp,amazon,aws
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Internet
16
+ Classifier: Topic :: Software Development
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: amazon
23
+ Requires-Dist: boto3; extra == "amazon"
24
+ Provides-Extra: google
25
+ Requires-Dist: google-cloud-pubsub; extra == "google"
26
+ Requires-Dist: google-cloud-storage; extra == "google"
27
+ Provides-Extra: all
28
+ Requires-Dist: boto3; extra == "all"
29
+ Requires-Dist: google-cloud-pubsub; extra == "all"
30
+ Requires-Dist: google-cloud-storage; extra == "all"
31
+
32
+ # Cloud Toolbox
33
+
34
+ ![Tests](https://github.com/DotzInc/cloud-toolbox/actions/workflows/tests.yml/badge.svg?event=push)
35
+ ![PyPI - Version](https://img.shields.io/pypi/v/cloudtoolbox)
36
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cloudtoolbox)
37
+
38
+ Decouple your applications from cloud providers with carefully crafted service interfaces.
39
+
40
+ ## Requirements
41
+
42
+ * Python 3.8+
43
+
44
+ ## Installation
45
+
46
+ To install Cloud Toolbox, use pip:
47
+
48
+ ```sh
49
+ pip install cloudtoolbox
50
+ ```
51
+
52
+ ### Extras
53
+
54
+ Cloud Toolbox offers the following optional dependencies for easy installation of provider SDKs:
55
+
56
+ * `cloudtoolbox[amazon]` - Installs the Amazon AWS SDK.
57
+ * `cloudtoolbox[google]` - Installs the Google Cloud SDK.
58
+ * `cloudtoolbox[all]` - Installs SDKs for both providers.
59
+
60
+ ## Example
61
+
62
+ Uploading a file to Google Cloud Storage.
63
+
64
+ ```python
65
+ from cloud import factory
66
+ from cloud.google.storage import Uploader
67
+
68
+ FileUploader = factory.storage_uploader(Uploader)
69
+
70
+ bucket = "my-bucket"
71
+ filename = "notes.txt"
72
+ filepath = f"/path/to/{filename}"
73
+
74
+ uploader = FileUploader()
75
+ uploader.upload(bucket, filename, filepath)
76
+ ```
77
+
78
+ Switching from Cloud Storage to Amazon S3.
79
+
80
+ ```python
81
+ # Replace this import
82
+ from cloud.google.storage import Uploader
83
+
84
+ # For this one
85
+ from cloud.amazon.s3 import Uploader
86
+ ```
@@ -6,6 +6,8 @@ cloud/factory.py
6
6
  cloud/protocols.py
7
7
  cloud/amazon/__init__.py
8
8
  cloud/amazon/s3.py
9
+ cloud/amazon/sns.py
10
+ cloud/amazon/sqs.py
9
11
  cloud/google/__init__.py
10
12
  cloud/google/pubsub.py
11
13
  cloud/google/storage.py
@@ -1,4 +1,9 @@
1
1
 
2
+ [all]
3
+ boto3
4
+ google-cloud-pubsub
5
+ google-cloud-storage
6
+
2
7
  [amazon]
3
8
  boto3
4
9
 
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cloudtoolbox"
7
+ version = "0.3.0"
8
+ description = "Toolbox for the cloud."
9
+ readme = { file = "README.md", content-type = "text/markdown" }
10
+ authors = [{ name = "Dotz Developers", email = "devs-dotz@dotz.com" }]
11
+ keywords = ["cloud", "tools", "utilities", "google", "gcp", "amazon", "aws"]
12
+ classifiers = [
13
+ "Operating System :: OS Independent",
14
+ "Programming Language :: Python :: 3 :: Only",
15
+ "Programming Language :: Python :: 3.8",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Internet",
21
+ "Topic :: Software Development",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ ]
25
+ requires-python = ">=3.8"
26
+ dependencies = []
27
+
28
+ [project.urls]
29
+ Repository = "https://github.com/DotzInc/cloud-toolbox"
30
+
31
+ [project.optional-dependencies]
32
+ amazon = ["boto3"]
33
+ google = ["google-cloud-pubsub", "google-cloud-storage"]
34
+ all = ["boto3", "google-cloud-pubsub", "google-cloud-storage"]
35
+
36
+ [tool.ruff]
37
+ exclude = [".git", ".venv", ".ruff_cache"]
38
+
39
+ line-length = 100
40
+ indent-width = 4
41
+
42
+ [tool.ruff.lint]
43
+ select = ["E", "W", "F", "I", "B", "C4", "UP", "ARG001"]
44
+ ignore = ["E402", "E501", "W191", "B904"]
45
+ fixable = ["ALL"]
46
+ unfixable = []
47
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
48
+
49
+ [tool.ruff.format]
50
+ quote-style = "double"
51
+ indent-style = "space"
52
+ skip-magic-trailing-comma = false
53
+ line-ending = "auto"
@@ -0,0 +1,126 @@
1
+ import unittest
2
+ from unittest import mock
3
+
4
+ from cloud import factory
5
+ from cloud.amazon import s3, sns, sqs
6
+ from cloud.google import pubsub, storage
7
+ from cloud.protocols import MessagePublisher, StorageDownloader, StorageUploader, StorageURLSigner
8
+
9
+
10
+ class NotStorageUploader:
11
+ def upload_file(self):
12
+ pass
13
+
14
+
15
+ class NotStorageDownloader:
16
+ def download_file(self):
17
+ pass
18
+
19
+
20
+ class NotStorageURLSigner:
21
+ def sign_url(self):
22
+ pass
23
+
24
+
25
+ class DummyStorageURLSigner:
26
+ def generate_presigned_url(
27
+ self,
28
+ bucket_name: str,
29
+ source_filename: str,
30
+ expiration: int,
31
+ ) -> str:
32
+ return ""
33
+
34
+
35
+ class NotMessagePublisher:
36
+ def send_message(self):
37
+ pass
38
+
39
+
40
+ class TestStorageUploaderFactory(unittest.TestCase):
41
+ @mock.patch("google.cloud.storage.Client")
42
+ def test_google_uploader(self, _):
43
+ Uploader = factory.storage_uploader(storage.Uploader)
44
+ uploader = Uploader()
45
+
46
+ self.assertIsInstance(uploader, StorageUploader)
47
+ self.assertIsInstance(uploader, storage.Uploader)
48
+
49
+ @mock.patch("boto3.client")
50
+ def test_amazon_uploader(self, _):
51
+ Uploader = factory.storage_uploader(s3.Uploader)
52
+ uploader = Uploader()
53
+
54
+ self.assertIsInstance(uploader, StorageUploader)
55
+ self.assertIsInstance(uploader, s3.Uploader)
56
+
57
+ def test_uploader_protocol_validation(self):
58
+ error = "NotStorageUploader does not implement StorageUploader protocol"
59
+
60
+ with self.assertRaisesRegex(AssertionError, error):
61
+ factory.storage_uploader(NotStorageUploader) # type: ignore
62
+
63
+
64
+ class TestStorageDownloaderFactory(unittest.TestCase):
65
+ @mock.patch("google.cloud.storage.Client")
66
+ def test_google_downloader(self, _):
67
+ Downloader = factory.storage_downloader(storage.Downloader)
68
+ downloader = Downloader()
69
+
70
+ self.assertIsInstance(downloader, StorageDownloader)
71
+ self.assertIsInstance(downloader, storage.Downloader)
72
+
73
+ @mock.patch("boto3.client")
74
+ def test_amazon_downloader(self, _):
75
+ Downloader = factory.storage_downloader(s3.Downloader)
76
+ downloader = Downloader()
77
+
78
+ self.assertIsInstance(downloader, StorageDownloader)
79
+ self.assertIsInstance(downloader, s3.Downloader)
80
+
81
+ def test_downloader_protocol_validation(self):
82
+ error = "NotStorageDownloader does not implement StorageDownloader protocol"
83
+
84
+ with self.assertRaisesRegex(AssertionError, error):
85
+ factory.storage_downloader(NotStorageDownloader) # type: ignore
86
+
87
+
88
+ class TestStorageURLSigner(unittest.TestCase):
89
+ def test_urlsigner(self):
90
+ URLSigner = factory.storage_urlsigner(DummyStorageURLSigner)
91
+ urlsigner = URLSigner()
92
+
93
+ self.assertIsInstance(urlsigner, StorageURLSigner)
94
+ self.assertIsInstance(urlsigner, DummyStorageURLSigner)
95
+
96
+ def test_urlsigner_protocol_validation(self):
97
+ error = "NotStorageURLSigner does not implement StorageURLSigner protocol"
98
+
99
+ with self.assertRaisesRegex(AssertionError, error):
100
+ factory.storage_urlsigner(NotStorageURLSigner) # type: ignore
101
+
102
+
103
+ class TestMessagePublisherFactory(unittest.TestCase):
104
+ @mock.patch("google.cloud.pubsub_v1.PublisherClient")
105
+ def test_google_publisher(self, _):
106
+ Publisher = factory.message_publisher(pubsub.Publisher)
107
+ publisher = Publisher()
108
+
109
+ self.assertIsInstance(publisher, MessagePublisher)
110
+ self.assertIsInstance(publisher, pubsub.Publisher)
111
+
112
+ @mock.patch("boto3.client")
113
+ def test_amazon_publisher(self, _):
114
+ for cls in (sns.Publisher, sqs.Publisher):
115
+ with self.subTest(class_name=cls.__name__):
116
+ Publisher = factory.message_publisher(cls)
117
+ publisher = Publisher()
118
+
119
+ self.assertIsInstance(publisher, MessagePublisher)
120
+ self.assertIsInstance(publisher, cls)
121
+
122
+ def test_publisher_protocol_validation(self):
123
+ error = "NotMessagePublisher does not implement MessagePublisher protocol"
124
+
125
+ with self.assertRaisesRegex(AssertionError, error):
126
+ factory.message_publisher(NotMessagePublisher) # type: ignore
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: cloudtoolbox
3
- Version: 0.1.0
4
- Summary: Toolbox for the cloud.
5
- Author-email: Dotz Developers <devs-dotz@dotz.com>
6
- Keywords: cloud,tools,utilities,google,gcp,amazon,aws
7
- Classifier: Programming Language :: Python :: 3
8
- Requires-Python: >=3.8
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Provides-Extra: amazon
12
- Requires-Dist: boto3; extra == "amazon"
13
- Provides-Extra: google
14
- Requires-Dist: google-cloud-pubsub; extra == "google"
15
- Requires-Dist: google-cloud-storage; extra == "google"
16
-
17
- # Cloud Toolbox
18
-
19
- Decouple your applications from cloud providers with carefully crafted service interfaces.
@@ -1,3 +0,0 @@
1
- # Cloud Toolbox
2
-
3
- Decouple your applications from cloud providers with carefully crafted service interfaces.
@@ -1,11 +0,0 @@
1
- from typing import Any
2
-
3
- import boto3
4
-
5
-
6
- class Uploader:
7
- def __init__(self, *args: Any, **kwargs: Any) -> None:
8
- self.client = boto3.client("s3", *args, **kwargs)
9
-
10
- def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None:
11
- self.client.upload_file(source_filename, bucket_name, destination_filename)
@@ -1,29 +0,0 @@
1
- from typing import Any, Callable, Type
2
-
3
- from cloud.protocols import MessagePublisher, StorageUploader
4
-
5
- error_message = "%(class)s does not implement %(protocol)s protocol"
6
-
7
-
8
- def storage_uploader(
9
- uploader_class: Type[StorageUploader], *args: Any, **kwargs: Any
10
- ) -> Callable[[], StorageUploader]:
11
- error = error_message % {"class": uploader_class.__name__, "protocol": "StorageUploader"}
12
- assert issubclass(uploader_class, StorageUploader), error
13
-
14
- def maker() -> StorageUploader:
15
- return uploader_class(*args, **kwargs)
16
-
17
- return maker
18
-
19
-
20
- def message_publisher(
21
- publisher_class: Type[MessagePublisher], *args: Any, **kwargs: Any
22
- ) -> Callable[[], MessagePublisher]:
23
- error = error_message % {"class": publisher_class.__name__, "protocol": "MessagePublisher"}
24
- assert issubclass(publisher_class, MessagePublisher), error
25
-
26
- def maker() -> MessagePublisher:
27
- return publisher_class(*args, **kwargs)
28
-
29
- return maker
@@ -1,13 +0,0 @@
1
- from typing import Any
2
-
3
- from google.cloud import storage
4
-
5
-
6
- class Uploader:
7
- def __init__(self, *args: Any, **kwargs: Any) -> None:
8
- self.client = storage.Client(*args, **kwargs)
9
-
10
- def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None:
11
- bucket = self.client.bucket(bucket_name)
12
- blob = bucket.blob(destination_filename)
13
- blob.upload_from_filename(source_filename)
@@ -1,11 +0,0 @@
1
- from typing import Any, Protocol, runtime_checkable
2
-
3
-
4
- @runtime_checkable
5
- class StorageUploader(Protocol):
6
- def upload(self, bucket_name: str, destination_filename: str, source_filename: str) -> None: ...
7
-
8
-
9
- @runtime_checkable
10
- class MessagePublisher(Protocol):
11
- def publish(self, recipient: str, message: str, **attrs: Any) -> None: ...
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: cloudtoolbox
3
- Version: 0.1.0
4
- Summary: Toolbox for the cloud.
5
- Author-email: Dotz Developers <devs-dotz@dotz.com>
6
- Keywords: cloud,tools,utilities,google,gcp,amazon,aws
7
- Classifier: Programming Language :: Python :: 3
8
- Requires-Python: >=3.8
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Provides-Extra: amazon
12
- Requires-Dist: boto3; extra == "amazon"
13
- Provides-Extra: google
14
- Requires-Dist: google-cloud-pubsub; extra == "google"
15
- Requires-Dist: google-cloud-storage; extra == "google"
16
-
17
- # Cloud Toolbox
18
-
19
- Decouple your applications from cloud providers with carefully crafted service interfaces.
@@ -1,45 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "cloudtoolbox"
7
- version = "0.1.0"
8
- description = "Toolbox for the cloud."
9
- readme = {file = "README.md", content-type = "text/markdown"}
10
- authors = [
11
- {name = "Dotz Developers", email = "devs-dotz@dotz.com"},
12
- ]
13
- keywords = ["cloud", "tools", "utilities", "google", "gcp", "amazon", "aws"]
14
- classifiers = [
15
- "Programming Language :: Python :: 3",
16
- ]
17
- requires-python = ">=3.8"
18
- dependencies = []
19
-
20
- [project.optional-dependencies]
21
- amazon = ["boto3"]
22
- google = ["google-cloud-pubsub", "google-cloud-storage"]
23
-
24
- [tool.ruff]
25
- exclude = [
26
- ".git",
27
- ".venv",
28
- ".ruff_cache",
29
- ]
30
-
31
- line-length = 100
32
- indent-width = 4
33
-
34
- [tool.ruff.lint]
35
- select = ["E", "W", "F", "I", "B", "C4", "UP","ARG001"]
36
- ignore = ["E402", "E501", "W191", "B904"]
37
- fixable = ["ALL"]
38
- unfixable = []
39
- dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
40
-
41
- [tool.ruff.format]
42
- quote-style = "double"
43
- indent-style = "space"
44
- skip-magic-trailing-comma = false
45
- line-ending = "auto"
@@ -1,57 +0,0 @@
1
- import unittest
2
- from unittest import mock
3
-
4
- from cloud import factory
5
- from cloud.amazon import s3
6
- from cloud.google import pubsub, storage
7
- from cloud.protocols import MessagePublisher, StorageUploader
8
-
9
-
10
- class NotStorageUploader:
11
- def upload_file(self):
12
- pass
13
-
14
-
15
- class NotMessagePublisher:
16
- def send_message(self):
17
- pass
18
-
19
-
20
- class TestStorageUploaderFactory(unittest.TestCase):
21
- @mock.patch("google.cloud.storage.Client")
22
- def test_google_uploader(self, _):
23
- Uploader = factory.storage_uploader(storage.Uploader)
24
- uploader = Uploader()
25
-
26
- self.assertIsInstance(uploader, StorageUploader)
27
- self.assertIsInstance(uploader, storage.Uploader)
28
-
29
- @mock.patch("boto3.client")
30
- def test_amazon_uploader(self, _):
31
- Uploader = factory.storage_uploader(s3.Uploader)
32
- uploader = Uploader()
33
-
34
- self.assertIsInstance(uploader, StorageUploader)
35
- self.assertIsInstance(uploader, s3.Uploader)
36
-
37
- def test_uploader_protocol_validation(self):
38
- error = "NotStorageUploader does not implement StorageUploader protocol"
39
-
40
- with self.assertRaisesRegex(AssertionError, error):
41
- factory.storage_uploader(NotStorageUploader) # type: ignore
42
-
43
-
44
- class TestMessagePublisherFactory(unittest.TestCase):
45
- @mock.patch("google.cloud.pubsub_v1.PublisherClient")
46
- def test_google_publisher(self, _):
47
- Publisher = factory.message_publisher(pubsub.Publisher)
48
- publisher = Publisher()
49
-
50
- self.assertIsInstance(publisher, MessagePublisher)
51
- self.assertIsInstance(publisher, pubsub.Publisher)
52
-
53
- def test_publisher_protocol_validation(self):
54
- error = "NotMessagePublisher does not implement MessagePublisher protocol"
55
-
56
- with self.assertRaisesRegex(AssertionError, error):
57
- factory.message_publisher(NotMessagePublisher) # type: ignore
File without changes