pyaws-s3 1.0.3__tar.gz → 1.0.5__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.
- {pyaws_s3-1.0.3/pyaws_s3.egg-info → pyaws_s3-1.0.5}/PKG-INFO +4 -5
- pyaws_s3-1.0.5/pyaws_s3/__init__.py +1 -0
- pyaws_s3-1.0.5/pyaws_s3/s3.py +428 -0
- pyaws_s3-1.0.5/pyaws_s3/util.py +47 -0
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5/pyaws_s3.egg-info}/PKG-INFO +4 -5
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/pyaws_s3.egg-info/SOURCES.txt +3 -2
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/pyaws_s3.egg-info/requires.txt +1 -1
- pyaws_s3-1.0.5/pyaws_s3.egg-info/top_level.txt +1 -0
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/pyproject.toml +6 -3
- pyaws_s3-1.0.3/LICENSE.txt +0 -21
- pyaws_s3-1.0.3/pyaws_s3.egg-info/top_level.txt +0 -1
- pyaws_s3-1.0.3/setup.py +0 -54
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/LICENSE.md +0 -0
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/README.md +0 -0
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/pyaws_s3.egg-info/dependency_links.txt +0 -0
- {pyaws_s3-1.0.3 → pyaws_s3-1.0.5}/setup.cfg +0 -0
@@ -1,14 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyaws_s3
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.5
|
4
4
|
Summary: A Python package for AWS S3 utilities
|
5
|
-
Author: Giuseppe Zileni
|
6
5
|
Author-email: Giuseppe Zileni <giuseppe.zileni@gmail.com>
|
7
6
|
Keywords: aws,s3,utilities
|
8
7
|
Classifier: Programming Language :: Python :: 3
|
9
8
|
Classifier: Operating System :: OS Independent
|
10
9
|
Requires-Python: >=3.12
|
11
10
|
Description-Content-Type: text/markdown
|
11
|
+
License-File: LICENSE.md
|
12
12
|
Requires-Dist: aioboto3==14.3.0
|
13
13
|
Requires-Dist: aiobotocore==2.22.0
|
14
14
|
Requires-Dist: aiofiles==24.1.0
|
@@ -31,7 +31,7 @@ Requires-Dist: matplotlib==3.10.3
|
|
31
31
|
Requires-Dist: multidict==6.4.3
|
32
32
|
Requires-Dist: narwhals==1.39.1
|
33
33
|
Requires-Dist: numpy==2.2.5
|
34
|
-
Requires-Dist: packaging==
|
34
|
+
Requires-Dist: packaging==24.2
|
35
35
|
Requires-Dist: pandas==2.2.3
|
36
36
|
Requires-Dist: pillow==11.2.1
|
37
37
|
Requires-Dist: plotly==6.1.0
|
@@ -45,8 +45,7 @@ Requires-Dist: tzdata==2025.2
|
|
45
45
|
Requires-Dist: urllib3==2.4.0
|
46
46
|
Requires-Dist: wrapt==1.17.2
|
47
47
|
Requires-Dist: yarl==1.20.0
|
48
|
-
Dynamic:
|
49
|
-
Dynamic: requires-python
|
48
|
+
Dynamic: license-file
|
50
49
|
|
51
50
|
# PYAWS_S3
|
52
51
|
|
@@ -0,0 +1 @@
|
|
1
|
+
from .s3 import S3Client
|
@@ -0,0 +1,428 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import boto3
|
4
|
+
import aioboto3
|
5
|
+
import pandas as pd
|
6
|
+
import io
|
7
|
+
import matplotlib.pyplot as plt
|
8
|
+
from pandas.plotting import table
|
9
|
+
from typing import Any, Literal
|
10
|
+
from util import bytes_from_figure, html_from_figure
|
11
|
+
from fpdf import FPDF
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
FormatFile = Literal["png", "jpeg", "svg", "html", "xlsx", "csv", "pdf"]
|
16
|
+
|
17
|
+
class S3Client:
|
18
|
+
"""
|
19
|
+
A class to interact with AWS S3 for uploading and managing files.
|
20
|
+
"""
|
21
|
+
|
22
|
+
aws_access_key_id : str
|
23
|
+
aws_secret_access_key : str
|
24
|
+
region_name : str
|
25
|
+
bucket_name : str
|
26
|
+
|
27
|
+
def __init__(self, **kwargs: Any) -> None:
|
28
|
+
"""
|
29
|
+
Initialize the S3Client with AWS credentials and region.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
aws_access_key_id (str): AWS access key ID.
|
33
|
+
aws_secret_access_key (str): AWS secret access key.
|
34
|
+
region_name (str): AWS region name.
|
35
|
+
"""
|
36
|
+
# aws_access_key_id: str, aws_secret_access_key: str, region_name: str
|
37
|
+
self.aws_access_key_id = kwargs.get("aws_access_key_id", os.getenv("AWS_ACCESS_KEY_ID"))
|
38
|
+
self.aws_secret_access_key = kwargs.get("aws_secret_access_key", os.getenv("AWS_SECRET_ACCESS_KEY"))
|
39
|
+
self.region_name = kwargs.get("region_name", os.getenv("AWS_REGION"))
|
40
|
+
self.bucket_name = kwargs.get("bucket_name", os.getenv("AWS_BUCKET_NAME"))
|
41
|
+
|
42
|
+
async def _get_s3_client_async(self) -> Any:
|
43
|
+
"""
|
44
|
+
Get an asynchronous S3 client using the provided AWS credentials and region.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
aws_access_key_id (str): AWS access key ID.
|
48
|
+
aws_secret_access_key (str): AWS secret access key.
|
49
|
+
region_name (str): AWS region name.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
Any: Asynchronous S3 client object.
|
53
|
+
"""
|
54
|
+
session = aioboto3.Session()
|
55
|
+
return session.resource(
|
56
|
+
's3',
|
57
|
+
aws_access_key_id=self.aws_access_key_id,
|
58
|
+
aws_secret_access_key=self.aws_secret_access_key,
|
59
|
+
region_name=self.region_name
|
60
|
+
)
|
61
|
+
|
62
|
+
def _get_s3_client(self) -> Any:
|
63
|
+
"""
|
64
|
+
Get an asynchronous S3 client using the provided AWS credentials and region.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
aws_access_key_id (str): AWS access key ID.
|
68
|
+
aws_secret_access_key (str): AWS secret access key.
|
69
|
+
region_name (str): AWS region name.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
Any: Asynchronous S3 client object.
|
73
|
+
"""
|
74
|
+
return boto3.client(
|
75
|
+
's3',
|
76
|
+
aws_access_key_id=self.aws_access_key_id,
|
77
|
+
aws_secret_access_key=self.aws_secret_access_key,
|
78
|
+
region_name=self.region_name
|
79
|
+
)
|
80
|
+
|
81
|
+
def _get_s3_resource(self) -> Any:
|
82
|
+
"""
|
83
|
+
Get an S3 client using the provided AWS credentials and region.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
aws_access_key_id (str): AWS access key ID.
|
87
|
+
aws_secret_access_key (str): AWS secret access key.
|
88
|
+
region_name (str): AWS region name.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Any: S3 client object.
|
92
|
+
|
93
|
+
Raises:
|
94
|
+
Exception: If there is an error creating the S3 client.
|
95
|
+
"""
|
96
|
+
try:
|
97
|
+
|
98
|
+
return boto3.resource('s3',
|
99
|
+
aws_access_key_id=self.aws_access_key_id,
|
100
|
+
aws_secret_access_key=self.aws_secret_access_key,
|
101
|
+
region_name=self.region_name)
|
102
|
+
except Exception as e:
|
103
|
+
logger.error(f"Error getting S3 client: {str(e)}")
|
104
|
+
raise Exception(f"Error getting S3 client: {str(e)}")
|
105
|
+
|
106
|
+
def _create_url(self, s3_client, bucket_name: str, object_name: str) -> str:
|
107
|
+
"""
|
108
|
+
Generate a pre-signed URL for an S3 object.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
s3_client: The S3 client object.
|
112
|
+
bucket_name (str): The name of the S3 bucket.
|
113
|
+
object_name (str): The name of the S3 object.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
str: The pre-signed URL for the S3 object.
|
117
|
+
"""
|
118
|
+
temp_url = s3_client.generate_presigned_url(
|
119
|
+
'get_object',
|
120
|
+
Params={
|
121
|
+
'Bucket': bucket_name,
|
122
|
+
'Key': object_name
|
123
|
+
},
|
124
|
+
ExpiresIn=900 # 15 minutes
|
125
|
+
)
|
126
|
+
|
127
|
+
logger.info(f"Pre-signed URL: {temp_url}")
|
128
|
+
|
129
|
+
return temp_url
|
130
|
+
|
131
|
+
def upload_image(self, *args, **kwargs: Any) -> str:
|
132
|
+
"""
|
133
|
+
Upload a Plotly Figure as a PNG image to an S3 bucket and generate a pre-signed URL.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
fig (Figure): The Plotly Figure object to upload.
|
137
|
+
bucket_name (str): The name of the S3 bucket.
|
138
|
+
object_name (str): The name of the S3 object.
|
139
|
+
|
140
|
+
Keyword Args:
|
141
|
+
format_file (str): Format of the image. Defaults to 'png'.
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
str: Pre-signed URL for the uploaded image.
|
145
|
+
|
146
|
+
Raises:
|
147
|
+
Exception: If there is an error uploading the image.
|
148
|
+
"""
|
149
|
+
try:
|
150
|
+
|
151
|
+
fig = args[0] if len(args) > 0 else None
|
152
|
+
if fig is None:
|
153
|
+
raise Exception("Figure is None")
|
154
|
+
|
155
|
+
object_name = args[1] if len(args) > 1 else None
|
156
|
+
if object_name is None:
|
157
|
+
raise Exception("Object name is None")
|
158
|
+
|
159
|
+
format_file : FormatFile = kwargs.get("format_file", "svg")
|
160
|
+
mimetypes = "image/svg+xml"
|
161
|
+
|
162
|
+
if format_file not in ["png", "jpeg", "svg", "html"]:
|
163
|
+
raise Exception("Invalid format_file provided. Supported formats are: png, jpeg, svg, html")
|
164
|
+
if format_file == "png":
|
165
|
+
mimetypes = "image/png"
|
166
|
+
elif format_file == "jpeg":
|
167
|
+
mimetypes = "image/jpeg"
|
168
|
+
elif format_file == "svg":
|
169
|
+
mimetypes = "image/svg+xml"
|
170
|
+
elif format_file == "html":
|
171
|
+
mimetypes = "text/html"
|
172
|
+
else:
|
173
|
+
raise Exception("Invalid MIME type provided")
|
174
|
+
|
175
|
+
# Get S3 client and resource
|
176
|
+
s3_client = self._get_s3_client()
|
177
|
+
s3_resource = self._get_s3_resource()
|
178
|
+
|
179
|
+
if format_file == "html":
|
180
|
+
# Convert the figure to SVG
|
181
|
+
file_text = html_from_figure(fig)
|
182
|
+
# Upload the html text to s3
|
183
|
+
s3_resource.Bucket(self.bucket_name).Object(object_name).put(Body=file_text, ContentType=mimetypes)
|
184
|
+
else:
|
185
|
+
# Convert the figure to bytes
|
186
|
+
file_buffer = bytes_from_figure(fig, format_file=format_file)
|
187
|
+
# Upload the image bytes to S3
|
188
|
+
s3_resource.Bucket(self.bucket_name).Object(object_name).put(Body=file_buffer, ContentType=mimetypes)
|
189
|
+
|
190
|
+
# Generate and return a pre-signed URL for the uploaded image
|
191
|
+
return self._create_url(s3_client, self.bucket_name, object_name)
|
192
|
+
|
193
|
+
except Exception as e:
|
194
|
+
logger.error(f"Error uploading image: {str(e)}")
|
195
|
+
raise Exception(f"Error uploading image: {str(e)}")
|
196
|
+
|
197
|
+
def upload_from_dataframe(self, *args : Any, **kwargs: Any) -> str:
|
198
|
+
"""
|
199
|
+
Upload a DataFrame as an Excel file to an S3 bucket and generate a pre-signed URL.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
df (DataFrame): The DataFrame to upload.
|
203
|
+
**kwargs (Any): Additional keyword arguments for AWS credentials, bucket name, and object name.
|
204
|
+
Keyword Args:
|
205
|
+
format_file (str): Format of the file. Defaults to 'xlsx'.
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
str: Pre-signed URL for the uploaded file.
|
209
|
+
|
210
|
+
Raises:
|
211
|
+
Exception: If there is an error uploading the file.
|
212
|
+
"""
|
213
|
+
try:
|
214
|
+
|
215
|
+
df = args[0] if len(args) > 0 else None
|
216
|
+
if df is None:
|
217
|
+
raise Exception("Figure is None")
|
218
|
+
|
219
|
+
object_name = args[1] if len(args) > 1 else None
|
220
|
+
if object_name is None:
|
221
|
+
raise Exception("Object name is None")
|
222
|
+
|
223
|
+
format_file : FormatFile = kwargs.get("format_file", "csv")
|
224
|
+
|
225
|
+
if format_file not in ["xlsx", "csv", "pdf"]:
|
226
|
+
raise Exception("Invalid format_file provided. Supported formats are: xlsx, csv, pdf")
|
227
|
+
|
228
|
+
if format_file == "xlsx":
|
229
|
+
mimetypes = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
230
|
+
elif format_file == "csv":
|
231
|
+
mimetypes = "text/csv"
|
232
|
+
elif format_file == "pdf":
|
233
|
+
mimetypes = "application/pdf"
|
234
|
+
else:
|
235
|
+
raise Exception("Invalid MIME type provided")
|
236
|
+
|
237
|
+
s3_client = self._get_s3_client()
|
238
|
+
s3_resource = self._get_s3_resource()
|
239
|
+
|
240
|
+
# Create a file buffer
|
241
|
+
ext: str = ""
|
242
|
+
with io.BytesIO() as file_buffer:
|
243
|
+
if mimetypes == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
244
|
+
ext = "xlsx"
|
245
|
+
# Convert DataFrame to Excel
|
246
|
+
with pd.ExcelWriter(file_buffer, engine="openpyxl") as writer:
|
247
|
+
df.to_excel(writer, index=False)
|
248
|
+
elif mimetypes == "text/csv":
|
249
|
+
ext = "csv"
|
250
|
+
# Convert DataFrame to CSV
|
251
|
+
df.to_csv(file_buffer, index=False)
|
252
|
+
elif mimetypes == "application/pdf":
|
253
|
+
ext = "pdf"
|
254
|
+
# Convert DataFrame to PDF
|
255
|
+
fig, ax = plt.subplots(figsize=(12, 4)) # Set the size of the figure
|
256
|
+
ax.axis('tight')
|
257
|
+
ax.axis('off')
|
258
|
+
table(ax, df, loc='center', cellLoc='center', colWidths=[0.1] * len(df.columns))
|
259
|
+
plt.savefig(file_buffer, format='pdf')
|
260
|
+
|
261
|
+
file_buffer.seek(0)
|
262
|
+
# Append the file extension to the object name
|
263
|
+
object_name = f"{object_name}.{ext}"
|
264
|
+
# Upload the file to S3
|
265
|
+
s3_resource.Bucket(self.bucket_name).Object(object_name).put(Body=file_buffer, ContentType=mimetypes)
|
266
|
+
|
267
|
+
logger.info(f"Uploaded file to S3: {object_name}")
|
268
|
+
|
269
|
+
return self._create_url(s3_client, self.bucket_name, object_name)
|
270
|
+
except Exception as e:
|
271
|
+
logger.error(f"Error uploading file: {str(e)}")
|
272
|
+
raise Exception(f"Error uploading file: {str(e)}")
|
273
|
+
|
274
|
+
async def delete_all(self, filter : str | None = None) -> None:
|
275
|
+
"""
|
276
|
+
Delete all files from an S3 bucket.
|
277
|
+
|
278
|
+
Args:
|
279
|
+
filter (str | None): Optional filter to delete specific files. If None, all files will be deleted.
|
280
|
+
Raises:
|
281
|
+
Exception: If there is an error deleting the files.
|
282
|
+
"""
|
283
|
+
try:
|
284
|
+
s3_client = self._get_s3_client()
|
285
|
+
|
286
|
+
# List all objects in the bucket
|
287
|
+
objects = s3_client.list_objects_v2(Bucket=self.bucket_name)
|
288
|
+
|
289
|
+
# Check if the bucket contains any objects
|
290
|
+
if 'Contents' in objects:
|
291
|
+
for obj in objects['Contents']:
|
292
|
+
if filter in obj['Key']:
|
293
|
+
# Delete each object
|
294
|
+
s3_client.delete_object(Bucket=self.bucket_name, Key=obj['Key'])
|
295
|
+
print(f"Deleted {obj['Key']}")
|
296
|
+
except Exception as e:
|
297
|
+
logger.error(f"Error deleting files: {str(e)}")
|
298
|
+
raise Exception(f"Error deleting files: {str(e)}")
|
299
|
+
|
300
|
+
def upload_to_pdf(self, *args : Any) -> str:
|
301
|
+
"""
|
302
|
+
Export the given text as a PDF and upload it to the S3 bucket.
|
303
|
+
|
304
|
+
Args:
|
305
|
+
text (str): The text to write in the PDF.
|
306
|
+
object_name (str): The name of the S3 object.
|
307
|
+
Raises:
|
308
|
+
Exception: If there is an error exporting the PDF.
|
309
|
+
Returns:
|
310
|
+
str: Pre-signed URL for the uploaded PDF.
|
311
|
+
"""
|
312
|
+
try:
|
313
|
+
text = args[0] if len(args) > 0 else None
|
314
|
+
if text is None:
|
315
|
+
raise Exception("Text is None")
|
316
|
+
|
317
|
+
object_name = args[1] if len(args) > 1 else None
|
318
|
+
if object_name is None:
|
319
|
+
raise Exception("Object name is None")
|
320
|
+
|
321
|
+
mimetypes = "application/pdf"
|
322
|
+
s3_client = self._get_s3_client()
|
323
|
+
s3_resource = self._get_s3_resource()
|
324
|
+
|
325
|
+
pdf = FPDF()
|
326
|
+
pdf.add_page()
|
327
|
+
pdf.set_auto_page_break(auto=True, margin=15)
|
328
|
+
pdf.set_font("Arial", size=12)
|
329
|
+
for line in text.splitlines():
|
330
|
+
pdf.multi_cell(0, 10, line)
|
331
|
+
|
332
|
+
# Write PDF to a bytes buffer
|
333
|
+
with io.BytesIO() as pdf_buffer:
|
334
|
+
pdf_bytes = pdf.output(dest='S')
|
335
|
+
pdf_buffer.write(pdf_bytes)
|
336
|
+
pdf_buffer.seek(0)
|
337
|
+
|
338
|
+
s3_resource.Bucket(self.bucket_name).Object(object_name).put(
|
339
|
+
Body=pdf_buffer,
|
340
|
+
ContentType=mimetypes
|
341
|
+
)
|
342
|
+
return self._create_url(s3_client, self.bucket_name, object_name)
|
343
|
+
except Exception as e:
|
344
|
+
logger.error(f"Error exporting PDF: {str(e)}")
|
345
|
+
raise Exception(f"Error exporting PDF: {str(e)}")
|
346
|
+
|
347
|
+
def download(self, *args : Any, **kwargs : Any):
|
348
|
+
"""
|
349
|
+
Download a file from S3 bucket.
|
350
|
+
|
351
|
+
Args:
|
352
|
+
object_name (str): The name of the S3 object to download.
|
353
|
+
**kwargs (Any): Additional keyword arguments for local path and stream.
|
354
|
+
- local_path (str): Local path to save the downloaded file. If None, the file will be streamed.
|
355
|
+
- stream (bool): If True, the file will be streamed instead of saved locally.
|
356
|
+
Raises:
|
357
|
+
Exception: If there is an error downloading the file.
|
358
|
+
Returns:
|
359
|
+
str: The local path of the downloaded file.
|
360
|
+
"""
|
361
|
+
try:
|
362
|
+
object_name = args[0] if len(args) > 0 else None
|
363
|
+
if object_name is None:
|
364
|
+
raise Exception("Object name is None")
|
365
|
+
|
366
|
+
local_path = kwargs.get("local_path", None)
|
367
|
+
stream = kwargs.get("stream", False)
|
368
|
+
|
369
|
+
if not stream and local_path is None:
|
370
|
+
raise Exception("Local path is None if stream is False")
|
371
|
+
|
372
|
+
s3_client = self._get_s3_client()
|
373
|
+
response = s3_client.download_file(self.bucket_name, object_name, local_path)
|
374
|
+
|
375
|
+
if stream:
|
376
|
+
return response['Body'].read()
|
377
|
+
|
378
|
+
return local_path
|
379
|
+
except Exception as e:
|
380
|
+
logger.error(f"Error downloading file: {str(e)}")
|
381
|
+
raise Exception(f"Error downloading file: {str(e)}")
|
382
|
+
|
383
|
+
def list_files(self, *args : Any) -> list[str]:
|
384
|
+
"""
|
385
|
+
List all files in the S3 bucket.
|
386
|
+
|
387
|
+
Args:
|
388
|
+
filter (str | None): Optional filter to list specific files. If None, all files will be listed.
|
389
|
+
Raises:
|
390
|
+
Exception: If there is an error listing the files.
|
391
|
+
Returns:
|
392
|
+
list[str]: List of file names in the S3 bucket.
|
393
|
+
"""
|
394
|
+
try:
|
395
|
+
filter = args[0] if len(args) > 0 else None
|
396
|
+
s3_client = self._get_s3_client()
|
397
|
+
objects = s3_client.list_objects_v2(Bucket=self.bucket_name)
|
398
|
+
|
399
|
+
# Check if the bucket contains any objects
|
400
|
+
if 'Contents' in objects:
|
401
|
+
return [obj['Key'] for obj in objects['Contents'] if filter in obj['Key']]
|
402
|
+
else:
|
403
|
+
return []
|
404
|
+
except Exception as e:
|
405
|
+
logger.error(f"Error listing files: {str(e)}")
|
406
|
+
raise Exception(f"Error listing files: {str(e)}")
|
407
|
+
|
408
|
+
def delete_file(self, *args : Any) -> None:
|
409
|
+
"""
|
410
|
+
Delete a file from the S3 bucket.
|
411
|
+
|
412
|
+
Args:
|
413
|
+
object_name (str): The name of the S3 object to delete.
|
414
|
+
Raises:
|
415
|
+
Exception: If there is an error deleting the file.
|
416
|
+
"""
|
417
|
+
try:
|
418
|
+
object_name = args[0] if len(args) > 0 else None
|
419
|
+
if object_name is None:
|
420
|
+
raise Exception("Object name is None")
|
421
|
+
|
422
|
+
s3_client = self._get_s3_client()
|
423
|
+
s3_client.delete_object(Bucket=self.bucket_name, Key=object_name)
|
424
|
+
except Exception as e:
|
425
|
+
logger.error(f"Error deleting file: {str(e)}")
|
426
|
+
raise Exception(f"Error deleting file: {str(e)}")
|
427
|
+
|
428
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import io
|
2
|
+
import logging
|
3
|
+
from plotly.graph_objs import Figure
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
def bytes_from_figure(f: Figure, **kwargs) -> bytes:
|
8
|
+
"""
|
9
|
+
Convert a Plotly Figure to a PNG image as bytes.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
f (Figure): The Plotly Figure object to be converted.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
bytes: The PNG image data as bytes.
|
16
|
+
:param f: The Plotly Figure object to be converted into a PNG image.
|
17
|
+
"""
|
18
|
+
|
19
|
+
format_file = kwargs.get("format_file", "png") # The format of the image to be converted to
|
20
|
+
width = kwargs.get("width", 640) # The width of the image in pixels
|
21
|
+
height = kwargs.get("height", 480) # The height of the image in pixels
|
22
|
+
|
23
|
+
with io.BytesIO() as bytes_buffer:
|
24
|
+
f.write_image(bytes_buffer,
|
25
|
+
format=format_file,
|
26
|
+
width = width,
|
27
|
+
height = height) # Write the figure to the bytes buffer as a PNG image
|
28
|
+
bytes_buffer.seek(0) # Reset the buffer position to the beginning
|
29
|
+
return bytes_buffer.getvalue() # Return the bytes data
|
30
|
+
|
31
|
+
def html_from_figure(f: Figure) -> str:
|
32
|
+
"""
|
33
|
+
Convert a Plotly Figure to an HTML string.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
f (Figure): The Plotly Figure object to be converted.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
str: The HTML representation of the figure as a string.
|
40
|
+
"""
|
41
|
+
with io.BytesIO() as bytes_buffer:
|
42
|
+
# Wrap the BytesIO with a TextIOWrapper to handle strings
|
43
|
+
with io.TextIOWrapper(bytes_buffer, encoding='utf-8') as text_buffer:
|
44
|
+
f.write_html(text_buffer) # Write the figure to the text buffer
|
45
|
+
text_buffer.flush() # Ensure all data is written
|
46
|
+
bytes_buffer.seek(0) # Reset the buffer position to the beginning
|
47
|
+
return bytes_buffer.getvalue().decode('utf-8') # Decode bytes to string and return
|
@@ -1,14 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyaws_s3
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.5
|
4
4
|
Summary: A Python package for AWS S3 utilities
|
5
|
-
Author: Giuseppe Zileni
|
6
5
|
Author-email: Giuseppe Zileni <giuseppe.zileni@gmail.com>
|
7
6
|
Keywords: aws,s3,utilities
|
8
7
|
Classifier: Programming Language :: Python :: 3
|
9
8
|
Classifier: Operating System :: OS Independent
|
10
9
|
Requires-Python: >=3.12
|
11
10
|
Description-Content-Type: text/markdown
|
11
|
+
License-File: LICENSE.md
|
12
12
|
Requires-Dist: aioboto3==14.3.0
|
13
13
|
Requires-Dist: aiobotocore==2.22.0
|
14
14
|
Requires-Dist: aiofiles==24.1.0
|
@@ -31,7 +31,7 @@ Requires-Dist: matplotlib==3.10.3
|
|
31
31
|
Requires-Dist: multidict==6.4.3
|
32
32
|
Requires-Dist: narwhals==1.39.1
|
33
33
|
Requires-Dist: numpy==2.2.5
|
34
|
-
Requires-Dist: packaging==
|
34
|
+
Requires-Dist: packaging==24.2
|
35
35
|
Requires-Dist: pandas==2.2.3
|
36
36
|
Requires-Dist: pillow==11.2.1
|
37
37
|
Requires-Dist: plotly==6.1.0
|
@@ -45,8 +45,7 @@ Requires-Dist: tzdata==2025.2
|
|
45
45
|
Requires-Dist: urllib3==2.4.0
|
46
46
|
Requires-Dist: wrapt==1.17.2
|
47
47
|
Requires-Dist: yarl==1.20.0
|
48
|
-
Dynamic:
|
49
|
-
Dynamic: requires-python
|
48
|
+
Dynamic: license-file
|
50
49
|
|
51
50
|
# PYAWS_S3
|
52
51
|
|
@@ -0,0 +1 @@
|
|
1
|
+
pyaws_s3
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "pyaws_s3"
|
7
|
-
version = "1.0.
|
7
|
+
version = "1.0.5"
|
8
8
|
description = "A Python package for AWS S3 utilities"
|
9
9
|
authors = [
|
10
10
|
{ name="Giuseppe Zileni", email="giuseppe.zileni@gmail.com" }
|
11
11
|
]
|
12
12
|
readme = {file = "README.md", content-type = "text/markdown"}
|
13
13
|
requires-python = ">=3.12"
|
14
|
-
license-files = ["LICENSE"]
|
14
|
+
license-files = ["LICENSE.md"]
|
15
15
|
keywords = ["aws", "s3", "utilities"]
|
16
16
|
classifiers = ['Programming Language :: Python :: 3', 'Operating System :: OS Independent']
|
17
17
|
dependencies = [
|
@@ -37,7 +37,7 @@ dependencies = [
|
|
37
37
|
"multidict==6.4.3",
|
38
38
|
"narwhals==1.39.1",
|
39
39
|
"numpy==2.2.5",
|
40
|
-
"packaging==
|
40
|
+
"packaging==24.2",
|
41
41
|
"pandas==2.2.3",
|
42
42
|
"pillow==11.2.1",
|
43
43
|
"plotly==6.1.0",
|
@@ -52,3 +52,6 @@ dependencies = [
|
|
52
52
|
"wrapt==1.17.2",
|
53
53
|
"yarl==1.20.0"
|
54
54
|
]
|
55
|
+
|
56
|
+
[tool.setuptools]
|
57
|
+
packages = ["pyaws_s3"] # oppure usa "find:" se hai una struttura standard
|
pyaws_s3-1.0.3/LICENSE.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
MIT License
|
2
|
-
|
3
|
-
Copyright (c) 2025 Giuseppe Zileni
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
13
|
-
copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
SOFTWARE.
|
@@ -1 +0,0 @@
|
|
1
|
-
|
pyaws_s3-1.0.3/setup.py
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
from setuptools import setup, find_packages
|
2
|
-
|
3
|
-
setup(
|
4
|
-
name="pyaws_s3",
|
5
|
-
version="1.0.0",
|
6
|
-
description="A package for AWS S3 utilities",
|
7
|
-
author="Giuseppe Zileni",
|
8
|
-
author_email="giuseppe.zileni@gmail,com",
|
9
|
-
packages=find_packages(),
|
10
|
-
install_requires=[
|
11
|
-
"aioboto3==14.3.0",
|
12
|
-
"aiobotocore==2.22.0",
|
13
|
-
"aiofiles==24.1.0",
|
14
|
-
"aiohappyeyeballs==2.6.1",
|
15
|
-
"aiohttp==3.11.18",
|
16
|
-
"aioitertools==0.12.0",
|
17
|
-
"aiosignal==1.3.2",
|
18
|
-
"attrs==25.3.0",
|
19
|
-
"boto3==1.37.3",
|
20
|
-
"botocore==1.37.3",
|
21
|
-
"contourpy==1.3.2",
|
22
|
-
"cycler==0.12.1",
|
23
|
-
"fonttools==4.58.0",
|
24
|
-
"fpdf==1.7.2",
|
25
|
-
"frozenlist==1.6.0",
|
26
|
-
"idna==3.10",
|
27
|
-
"jmespath==1.0.1",
|
28
|
-
"kiwisolver==1.4.8",
|
29
|
-
"matplotlib==3.10.3",
|
30
|
-
"multidict==6.4.3",
|
31
|
-
"narwhals==1.39.1",
|
32
|
-
"numpy==2.2.5",
|
33
|
-
"packaging==25.0",
|
34
|
-
"pandas==2.2.3",
|
35
|
-
"pillow==11.2.1",
|
36
|
-
"plotly==6.1.0",
|
37
|
-
"propcache==0.3.1",
|
38
|
-
"pyparsing==3.2.3",
|
39
|
-
"python-dateutil==2.9.0.post0",
|
40
|
-
"pytz==2025.2",
|
41
|
-
"s3transfer==0.11.3",
|
42
|
-
"six==1.17.0",
|
43
|
-
"tzdata==2025.2",
|
44
|
-
"urllib3==2.4.0",
|
45
|
-
"wrapt==1.17.2",
|
46
|
-
"yarl==1.20.0"
|
47
|
-
],
|
48
|
-
python_requires=">=3.12",
|
49
|
-
classifiers=[
|
50
|
-
"Programming Language :: Python :: 3",
|
51
|
-
"License :: OSI Approved :: MIT License",
|
52
|
-
"Operating System :: OS Independent",
|
53
|
-
],
|
54
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|