bifrostsdk 1.0.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 (48) hide show
  1. bifrostsdk-1.0.0.dist-info/LICENSE +21 -0
  2. bifrostsdk-1.0.0.dist-info/METADATA +124 -0
  3. bifrostsdk-1.0.0.dist-info/RECORD +48 -0
  4. bifrostsdk-1.0.0.dist-info/WHEEL +5 -0
  5. bifrostsdk-1.0.0.dist-info/top_level.txt +1 -0
  6. byfrost/__init__.py +46 -0
  7. byfrost/__init__.pyi +81 -0
  8. byfrost/bifrost.py +148 -0
  9. byfrost/bifrost.pyi +36 -0
  10. byfrost/gcs/__init__.py +3 -0
  11. byfrost/gcs/__init__.pyi +3 -0
  12. byfrost/gcs/gcs.py +189 -0
  13. byfrost/gcs/gcs.pyi +65 -0
  14. byfrost/pinata/__init__.py +0 -0
  15. byfrost/pinata/__init__.pyi +0 -0
  16. byfrost/pinata/pinata.py +221 -0
  17. byfrost/pinata/pinata.pyi +54 -0
  18. byfrost/s3/__init__.py +0 -0
  19. byfrost/s3/__init__.pyi +0 -0
  20. byfrost/s3/s3.py +188 -0
  21. byfrost/s3/s3.pyi +65 -0
  22. byfrost/shared/__init__.py +3 -0
  23. byfrost/shared/config/__init__.py +29 -0
  24. byfrost/shared/config/__init__.pyi +29 -0
  25. byfrost/shared/config/option.py +21 -0
  26. byfrost/shared/config/provider.py +22 -0
  27. byfrost/shared/config/request.py +24 -0
  28. byfrost/shared/config/url.py +24 -0
  29. byfrost/shared/errors/__init__.py +16 -0
  30. byfrost/shared/errors/__init__.pyi +16 -0
  31. byfrost/shared/errors/color.py +18 -0
  32. byfrost/shared/errors/constant.py +30 -0
  33. byfrost/shared/errors/interface.py +21 -0
  34. byfrost/shared/errors/interface.pyi +21 -0
  35. byfrost/shared/errors/loga.py +28 -0
  36. byfrost/shared/errors/loga.pyi +21 -0
  37. byfrost/shared/request/__init__.py +1 -0
  38. byfrost/shared/request/__init__.pyi +1 -0
  39. byfrost/shared/request/builder.py +51 -0
  40. byfrost/shared/types/__init__.py +1 -0
  41. byfrost/shared/types/dataclass/__init__.py +4 -0
  42. byfrost/shared/types/dataclass/__init__.pyi +4 -0
  43. byfrost/shared/types/dataclass/bridge.py +155 -0
  44. byfrost/shared/types/dataclass/file.py +222 -0
  45. byfrost/shared/types/typeddict/__init__.py +12 -0
  46. byfrost/shared/types/typeddict/__init__.pyi +12 -0
  47. byfrost/shared/types/typeddict/bridge.py +85 -0
  48. byfrost/shared/types/typeddict/file.py +203 -0
byfrost/gcs/gcs.py ADDED
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Bifrost interface for Google Cloud Storage"""
4
+
5
+ from byfrost.shared import errors
6
+ from byfrost.shared.config import provider
7
+ from google.cloud import storage
8
+ from ..shared.types.dataclass import file as bfile
9
+ from ..shared.types.dataclass import bridge
10
+ from typing import List, Tuple
11
+ from ..shared.errors.interface import BifrostError
12
+ from ..shared.config import option
13
+ from ..shared.errors import loga
14
+
15
+ import os
16
+
17
+
18
+ class GoogleCloudStorage:
19
+ def __init__(self, bc: bridge.BridgeConfig, client: storage.Client):
20
+ self.provider = provider.providers[bc.provider]
21
+ self.default_bucket = bc.default_bucket
22
+ self.credentials_file = bc.credentials_file
23
+ self.project = bc.project
24
+ self.default_timeout = bc.default_timeout
25
+ self.client = client
26
+ self.enable_debug = bc.enable_debug
27
+ self.public_read = bc.public_read
28
+ self.use_async = bc.use_async
29
+
30
+ def upload_file(
31
+ self, file_face: bfile.File
32
+ ) -> Tuple[bfile.UploadedFile, BifrostError]:
33
+ """upload_file uploads a file to Google Cloud Storage and returns an error if one occurs."""
34
+ if file_face is None:
35
+ return None, BifrostError("file is none", errors.ErrBadRequest)
36
+
37
+ # verify that the file is derived from File
38
+ if not isinstance(file_face, bfile.File):
39
+ return None, BifrostError(
40
+ "invalid file type: {}".format(type(file_face)), errors.ErrBadRequest
41
+ )
42
+
43
+ # verify that the file has a valid path
44
+ if file_face.path is None or file_face.path == "":
45
+ return None, BifrostError("no path specified", errors.ErrBadRequest)
46
+
47
+ if file_face.filename is None or file_face.filename == "":
48
+ file_face.filename = os.path.basename(file_face.path)
49
+
50
+ # verify that the file has a valid options
51
+ if file_face.options is None:
52
+ file_face.options = bfile.Options()
53
+
54
+ # verify the connection
55
+ if not self.is_connected():
56
+ return None, BifrostError(
57
+ "no active Google Cloud Storage client", errors.ErrClientError
58
+ )
59
+
60
+ # verify that the file has a valid acl
61
+ if (
62
+ file_face.options.acl is None
63
+ or file_face.options.acl == ""
64
+ and self.public_read
65
+ ):
66
+ file_face.options.acl = option.ACLPublicRead
67
+
68
+ # verify that the file has a valid metadata
69
+ if file_face.options.metadata is None:
70
+ file_face.options.metadata = {}
71
+
72
+ # upload file to Google Cloud Storage
73
+ bucket = self.client.bucket(self.default_bucket)
74
+
75
+ blob = bucket.blob(file_face.filename)
76
+ blob.metadata = file_face.options.metadata
77
+ blob.upload_from_filename(file_face.path)
78
+
79
+ # configure upload options
80
+ if file_face.options.acl == option.ACLPublicRead:
81
+ blob.make_public()
82
+ elif file_face.options.acl == option.ACLPrivate:
83
+ blob.make_private()
84
+
85
+ # return the uploaded file
86
+ #
87
+ return (
88
+ bfile.UploadedFile(
89
+ name=file_face.filename,
90
+ bucket=self.default_bucket,
91
+ path=file_face.path,
92
+ size=blob.size,
93
+ url=blob.public_url,
94
+ preview=blob.public_url,
95
+ provider_object=blob,
96
+ ),
97
+ None,
98
+ )
99
+
100
+ def upload_multi_file(
101
+ self,
102
+ multi_face: bfile.MultiFile,
103
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
104
+ """upload multi file uploads multiple files to Google Cloud Storage and returns an error if one occurs."""
105
+ if multi_face is None:
106
+ return None, BifrostError("multi file is none", errors.ErrBadRequest)
107
+
108
+ # verify that the multi file is derived from MultiFile
109
+ if not isinstance(multi_face, bfile.MultiFile):
110
+ return None, BifrostError(
111
+ "invalid multi file type: {}".format(type(multi_face)),
112
+ errors.ErrBadRequest,
113
+ )
114
+
115
+ # verify that the multi file has a valid files
116
+ if multi_face.files is None or len(multi_face.files) == 0:
117
+ return None, BifrostError("no files specified", errors.ErrBadRequest)
118
+
119
+ # verify the connection
120
+ if not self.is_connected():
121
+ return None, BifrostError(
122
+ "no active Google Cloud Storage client", errors.ErrClientError
123
+ )
124
+
125
+ # upload files to Google Cloud Storage
126
+ uploaded_files = []
127
+
128
+ for file_face in multi_face.files:
129
+ # verify that the multi file has a valid global options
130
+ if multi_face.global_options is not None:
131
+ # for each option in the global option dataclass, if it is not present in the file option dataclass, then add it to the file option dataclass
132
+ for option in multi_face.global_options.__dataclass_fields__:
133
+ if not hasattr(file_face.options, option):
134
+ setattr(
135
+ file_face.options,
136
+ option,
137
+ getattr(multi_face.global_options, option),
138
+ )
139
+
140
+ uploaded_file, err = self.upload_file(file_face)
141
+
142
+ if err is not None:
143
+ if self.enable_debug:
144
+ loga.error(
145
+ "Upload for file at path %s failed with err: %s",
146
+ file_face.path,
147
+ err.message,
148
+ )
149
+ uploaded_files.append(
150
+ bfile.UploadedFile(
151
+ error=err, name=file_face.filename, path=file_face.path
152
+ )
153
+ )
154
+ continue
155
+ uploaded_files.append(uploaded_file)
156
+ return uploaded_files, None
157
+
158
+ def disconnect(self) -> BifrostError:
159
+ """disconnect closes the Google Cloud Storage client connection and returns an error if one occurs."""
160
+ if self.client is None:
161
+ return None
162
+
163
+ self.client.close()
164
+ return None
165
+
166
+ def config(self) -> bridge.BridgeConfig:
167
+ """config returns the provider configuration."""
168
+ return bridge.BridgeConfig(
169
+ provider=self.provider,
170
+ zone=self.zone,
171
+ default_bucket=self.default_bucket,
172
+ credentials_file=self.credentials_file,
173
+ project=self.project,
174
+ default_timeout=self.default_timeout,
175
+ enable_debug=self.enable_debug,
176
+ public_read=self.public_read,
177
+ use_async=self.use_async,
178
+ )
179
+
180
+ def is_connected(self) -> bool:
181
+ """is_connected returns true if there is an active connection to the provider."""
182
+ return self.client is not None
183
+
184
+ def upload_folder(
185
+ self,
186
+ fold_face: bfile.MultiFile,
187
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
188
+ """upload_folder uploads a folder to the Google Cloud Storage and returns an error if one occurs."""
189
+ ...
byfrost/gcs/gcs.pyi ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ The RainbowBridge interface for Google Cloud Storage
4
+
5
+ All providers must implement this interface completely
6
+ """
7
+
8
+ from ..shared.types.dataclass import file as bfile
9
+ from ..shared.types.dataclass import bridge
10
+ from typing import List, Tuple
11
+ from ..shared.errors.interface import BifrostError
12
+ from google.cloud import storage
13
+
14
+ class GoogleCloudStorage:
15
+ """GoogleCloudStorage is the Google Cloud Storage class"""
16
+
17
+ def __init__(self, bc: bridge.BridgeConfig, client: storage.Client):
18
+ """__init__ initializes the GoogleCloudStorage class."""
19
+ ...
20
+ def upload_file(
21
+ self, file_face: bfile.File
22
+ ) -> Tuple[bfile.UploadedFile, BifrostError]:
23
+ """
24
+ upload_file uploads a file to the provider storage and returns an error if one occurs.
25
+
26
+ Note: for some providers, upload_file requires that a default bucket be set in bifrosbfile.BridgeConfig.
27
+ """
28
+ ...
29
+ def upload_multi_file(
30
+ self,
31
+ multi_face: bfile.MultiFile,
32
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
33
+ """
34
+ upload_multi_file uploads mutliple files to the provider storage and returns an error if one occurs. If any of the uploads fail, the error is appended to the []UploadedFile.Error and also logged when debug is enabled while the rest of the uploads continue.
35
+
36
+ Note: for some providers, UploadMultiFile requires that a default bucket be set in bifrosbfile.BridgeConfig.
37
+ """
38
+ ...
39
+ def disconnect(self) -> BifrostError:
40
+ """
41
+ disconnect closes the provider client connection and returns an error if one occurs.
42
+
43
+ Disconnect should only be called when the connection is no longer needed.
44
+ """
45
+ ...
46
+ def config(self) -> bridge.BridgeConfig:
47
+ """
48
+ config returns the provider configuration.
49
+ """
50
+ ...
51
+ def is_connected(self) -> bool:
52
+ """
53
+ is_connected returns true if there is an active connection to the provider.
54
+ """
55
+ ...
56
+ def upload_folder(
57
+ self,
58
+ fold_face: bfile.MultiFile,
59
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
60
+ """
61
+ upload_folder uploads a folder to the provider storage and returns an error if one occurs.
62
+
63
+ Note: for some providers, upload_folder requires that a default bucket be set in bifrosbfile.BridgeConfig.
64
+ """
65
+ ...
File without changes
File without changes
@@ -0,0 +1,221 @@
1
+ """Bifrost interface for Pinata Cloud."""
2
+
3
+ from byfrost.shared import errors
4
+ from byfrost.shared.config import provider
5
+ from ..shared.types.dataclass import file as bfile
6
+ from ..shared.types.dataclass import bridge
7
+ from typing import List, Tuple
8
+ from ..shared.errors.interface import BifrostError
9
+ from ..shared.config import option, request, url
10
+ from ..shared.errors import loga
11
+ from ..shared.request.builder import Client, new_client
12
+ import os
13
+ import requests
14
+ import json
15
+
16
+
17
+ class PinataCloud:
18
+ def __init__(self, bc: bridge.BridgeConfig, client: Client):
19
+ self.provider = provider.providers[bc.provider]
20
+ self.default_bucket = bc.default_bucket
21
+ self.pinata_jwt = bc.pinata_jwt
22
+ self.default_timeout = bc.default_timeout
23
+ self.client = client
24
+ self.enable_debug = bc.enable_debug
25
+ self.public_read = bc.public_read
26
+ self.use_async = bc.use_async
27
+
28
+ def upload_file(
29
+ self, file_face: bfile.File
30
+ ) -> Tuple[bfile.UploadedFile, BifrostError]:
31
+ """upload_file uploads a file to Pinata Storage and returns an error if one occurs."""
32
+ if file_face is None:
33
+ return None, BifrostError("file is none", errors.ErrBadRequest)
34
+
35
+ # verify that the file is derived from File
36
+ if not isinstance(file_face, bfile.File):
37
+ return None, BifrostError(
38
+ "invalid file type: {}".format(type(file_face)), errors.ErrBadRequest
39
+ )
40
+
41
+ # verify that the file has a valid path
42
+ if file_face.path is None or file_face.path == "":
43
+ return None, BifrostError("no path specified", errors.ErrBadRequest)
44
+
45
+ if not os.path.exists(file_face.path):
46
+ raise BifrostError(
47
+ f"file does not exist: {file_face.path}", errors.ErrBadRequest
48
+ )
49
+
50
+ if file_face.filename is None or file_face.filename == "":
51
+ file_face.filename = os.path.basename(file_face.path)
52
+
53
+ # verify that the file has a valid options
54
+ if file_face.options is None:
55
+ file_face.options = bfile.Options()
56
+
57
+ # verify that the file has a valid metadata
58
+ if file_face.options.metadata is None:
59
+ file_face.options.metadata = {}
60
+
61
+ # verify the connection
62
+ if not self.is_connected():
63
+ return None, BifrostError(
64
+ "no active Pinata Storage client", errors.ErrClientError
65
+ )
66
+
67
+ param = bfile.Param(
68
+ files=[
69
+ bfile.ParamFile(
70
+ path=file_face.path,
71
+ key="file",
72
+ name=file_face.filename,
73
+ )
74
+ ],
75
+ data=[],
76
+ )
77
+
78
+ # Configure upload options
79
+ for k, v in file_face.options.__dataclass_fields__.items():
80
+ if k == option.OptPinata:
81
+ try:
82
+ opt = json.dumps(v)
83
+ param.data.append(bfile.ParamData(key=OptPinata, value=opt))
84
+ except json.JSONDecodeError as e:
85
+ return None, BifrostError(
86
+ f"failed to parse pinata options: {str(e)}",
87
+ errors.ErrBadRequest,
88
+ )
89
+
90
+ elif k == provider.PinataCloud + option.OptMetadata.capitalize():
91
+ try:
92
+ m = json.dumps(v)
93
+ param.data.append(bfile.ParamData(key=k, value=m))
94
+ except json.JSONDecodeError as e:
95
+ return None, BifrostError(
96
+ f"failed to parse pinata metadata: {str(e)}",
97
+ errors.ErrBadRequest,
98
+ )
99
+
100
+ response, error = self.client.post_form(url.URLPinataPinFile, param)
101
+
102
+ if error is not None:
103
+ print(error)
104
+ return None, BifrostError("failed to upload file", errors.ErrBadRequest)
105
+
106
+ if "Error" in response and response["Error"] != "":
107
+ return None, BifrostError(
108
+ f"failed to upload file: {response['Error']}", errors.ErrBadRequest
109
+ )
110
+
111
+ return (
112
+ bfile.UploadedFile(
113
+ size=response["PinSize"],
114
+ cid=response["IpfsHash"],
115
+ preview=url.URLPinataGateway.format(response["IpfsHash"]),
116
+ provider_object=response,
117
+ name=os.path.basename(file_face.path),
118
+ path=file_face.path,
119
+ url=url.URLPinataGateway.format(response["IpfsHash"]),
120
+ ),
121
+ None,
122
+ )
123
+
124
+ def upload_multi_file(
125
+ self,
126
+ multi_face: bfile.MultiFile,
127
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
128
+ """upload multi file uploads multiple files to Pinata Storage and returns an error if one occurs."""
129
+ if multi_face is None:
130
+ return None, BifrostError("multi file is none", errors.ErrBadRequest)
131
+
132
+ # verify that the multi file is derived from MultiFile
133
+ if not isinstance(multi_face, bfile.MultiFile):
134
+ return None, BifrostError(
135
+ "invalid multi file type: {}".format(type(multi_face)),
136
+ errors.ErrBadRequest,
137
+ )
138
+
139
+ # verify that the multi file has a valid files
140
+ if multi_face.files is None or len(multi_face.files) == 0:
141
+ return None, BifrostError("no files specified", errors.ErrBadRequest)
142
+
143
+ # verify the connection
144
+ if not self.is_connected():
145
+ return None, BifrostError(
146
+ "no active Pinata Cloud client", errors.ErrClientError
147
+ )
148
+
149
+ # upload files to Google Cloud Storage
150
+ uploaded_files = []
151
+
152
+ for file_face in multi_face.files:
153
+ # verify that the multi file has a valid global options
154
+ if multi_face.global_options is not None:
155
+ # for each option in the global option dataclass, if it is not present in the file option dataclass, then add it to the file option dataclass
156
+ for option in multi_face.global_options.__dataclass_fields__:
157
+ if not hasattr(file_face.options, option):
158
+ setattr(
159
+ file_face.options,
160
+ option,
161
+ getattr(multi_face.global_options, option),
162
+ )
163
+
164
+ uploaded_file, err = self.upload_file(file_face)
165
+
166
+ if err is not None:
167
+ if self.enable_debug:
168
+ loga.error(
169
+ "Upload for file at path %s failed with err: %s",
170
+ file_face.path,
171
+ err.message,
172
+ )
173
+ uploaded_files.append(
174
+ bfile.UploadedFile(
175
+ error=err, name=file_face.filename, path=file_face.path
176
+ )
177
+ )
178
+ continue
179
+ uploaded_files.append(uploaded_file)
180
+ return uploaded_files, None
181
+
182
+ def preflight(self) -> BifrostError:
183
+ """preflight attempts to authenticate with Pinata and returns an error if one occurs."""
184
+ try:
185
+ req = requests.Request(
186
+ request.MethodGet,
187
+ url.URLPinataAuth,
188
+ headers=self.client.session.headers,
189
+ ).prepare()
190
+ res = self.client.session.send(req)
191
+ if res.json()["message"] == "":
192
+ return BifrostError(res.json()["error"], errors.ErrUnauthorized)
193
+ return None
194
+ except Exception as e:
195
+ return BifrostError(str(e), errors.ErrClientError)
196
+
197
+ def disconnect(self) -> BifrostError:
198
+ """disconnect closes the Pinata Storage client connection and returns an error if one occurs."""
199
+ if self.client is None:
200
+ return None
201
+
202
+ def config(self) -> bridge.BridgeConfig:
203
+ """config returns the provider configuration."""
204
+ return bridge.BridgeConfig(
205
+ provider=self.provider,
206
+ default_timeout=self.default_timeout,
207
+ enable_debug=self.enable_debug,
208
+ public_read=self.public_read,
209
+ use_async=self.use_async,
210
+ )
211
+
212
+ def is_connected(self) -> bool:
213
+ """is_connected returns true if there is an active connection to the provider."""
214
+ return self.client is not None
215
+
216
+ def upload_folder(
217
+ self,
218
+ fold_face: bfile.MultiFile,
219
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
220
+ """upload_folder uploads a folder to Pinata Storage and returns an error if one occurs."""
221
+ ...
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ The RainbowBridge interface for Pinata Cloud
4
+
5
+ All providers must implement this interface completely
6
+ """
7
+
8
+ """Bifrost interface for Pinata Cloud."""
9
+
10
+ from byfrost.shared import errors
11
+ from byfrost.shared.config import provider
12
+ from ..shared.types.dataclass import file as bfile
13
+ from ..shared.types.dataclass import bridge
14
+ from typing import List, Tuple
15
+ from ..shared.errors.interface import BifrostError
16
+ from ..shared.config import option, request, url
17
+ from ..shared.errors import loga
18
+ from ..shared.request.builder import Client
19
+
20
+ class PinataCloud:
21
+ """PinataCloud is the Google Cloud Storage class"""
22
+
23
+ def __init__(self, bc: bridge.BridgeConfig, client: Client):
24
+ """__init__ initializes the GoogleCloudStorage class."""
25
+ ...
26
+ def upload_file(
27
+ self, file_face: bfile.File
28
+ ) -> Tuple[bfile.UploadedFile, BifrostError]:
29
+ """upload_file uploads a file to Pinata Storage and returns an error if one occurs."""
30
+ ...
31
+ def upload_multi_file(
32
+ self,
33
+ multi_face: bfile.MultiFile,
34
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
35
+ """upload multi file uploads multiple files to Pinata Storage and returns an error if one occurs."""
36
+ ...
37
+ def preflight(self) -> BifrostError:
38
+ """preflight attempts to authenticate with Pinata and returns an error if one occurs."""
39
+ ...
40
+ def disconnect(self) -> BifrostError:
41
+ """disconnect closes the Pinata Storage client connection and returns an error if one occurs."""
42
+ ...
43
+ def config(self) -> bridge.BridgeConfig:
44
+ """config returns the provider configuration."""
45
+ ...
46
+ def is_connected(self) -> bool:
47
+ """is_connected returns true if there is an active connection to the provider."""
48
+ ...
49
+ def upload_folder(
50
+ self,
51
+ fold_face: bfile.MultiFile,
52
+ ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
53
+ """upload_folder uploads a folder to Pinata Storage and returns an error if one occurs."""
54
+ ...
byfrost/s3/__init__.py ADDED
File without changes
File without changes