PyS3Uploader 0.2.2__py3-none-any.whl → 0.2.4a0__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.

Potentially problematic release.


This version of PyS3Uploader might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyS3Uploader
3
- Version: 0.2.2
3
+ Version: 0.2.4a0
4
4
  Summary: Python module to upload objects to an S3 bucket.
5
5
  Author-email: Vignesh Rao <svignesh1793@gmail.com>
6
6
  License: MIT License
@@ -40,6 +40,7 @@ Requires-Python: >=3.11
40
40
  Description-Content-Type: text/markdown
41
41
  License-File: LICENSE
42
42
  Requires-Dist: boto3==1.40.*
43
+ Requires-Dist: python-dotenv==1.1.*
43
44
  Requires-Dist: tqdm==4.67.*
44
45
  Provides-Extra: dev
45
46
  Requires-Dist: sphinx==5.1.1; extra == "dev"
@@ -90,7 +91,7 @@ if __name__ == '__main__':
90
91
  wrapper = s3.Uploader(
91
92
  bucket_name="BUCKET_NAME",
92
93
  upload_dir="FULL_PATH_TO_UPLOAD",
93
- exclude_path="PART_OF_UPLOAD_DIR_TO_EXCLUDE"
94
+ exclude_prefix="PART_OF_UPLOAD_DIR_TO_EXCLUDE"
94
95
  )
95
96
  wrapper.run_in_parallel()
96
97
  ```
@@ -103,7 +104,7 @@ if __name__ == '__main__':
103
104
  wrapper = s3.Uploader(
104
105
  bucket_name="BUCKET_NAME",
105
106
  upload_dir="FULL_PATH_TO_UPLOAD",
106
- exclude_path="PART_OF_UPLOAD_DIR_TO_EXCLUDE"
107
+ exclude_prefix="PART_OF_UPLOAD_DIR_TO_EXCLUDE"
107
108
  )
108
109
  wrapper.run()
109
110
  ```
@@ -114,10 +115,13 @@ if __name__ == '__main__':
114
115
 
115
116
  #### Optional kwargs
116
117
  - **s3_prefix** - S3 object prefix for each file. Defaults to ``None``
117
- - **exclude_path** - Path in ``upload_dir`` that has to be excluded in object keys. Defaults to `None`
118
+ - **exclude_prefix** - Path in ``upload_dir`` that has to be excluded in object keys. Defaults to `None`
118
119
  - **skip_dot_files** - Boolean flag to skip dot files. Defaults to ``True``
119
120
  - **overwrite** - Boolean flag to overwrite files present in S3. Defaults to ``False``
121
+ - **file_exclusion** - Sequence of files to exclude during upload. Defaults to ``None``
122
+ - **folder_exclusion** - Sequence of directories to exclude during upload. Defaults to ``None``
120
123
  - **logger** - Bring your own custom pre-configured logger. Defaults to on-screen logging.
124
+ - **env_file** – Path to a `.env` file for loading environment variables. Defaults to scanning the current directory.
121
125
  <br><br>
122
126
  - **region_name** - AWS region name. Defaults to the env var `AWS_DEFAULT_REGION`
123
127
  - **profile_name** - AWS profile name. Defaults to the env var `PROFILE_NAME`
@@ -0,0 +1,11 @@
1
+ s3/__init__.py,sha256=qPNHkWhXCMoofZe0kidyKkQ8YX3Ke0U35ViBo-oEWp0,68
2
+ s3/exceptions.py,sha256=hH3jlMOe8yjBatQK9EdndWZz4QESU74KSY_iDhQ37SY,2585
3
+ s3/logger.py,sha256=oH540oq8jY723jA4lDWlgfFPLbNgGXTkDwFpB7TLO_o,1196
4
+ s3/tree.py,sha256=DiQ2ekMMaj2m_P3-iKkEqSuJCJZ_UZxcAwHtAoPVa5c,1824
5
+ s3/uploader.py,sha256=VZOajOlWLWU-E34qS40YH8VUH_RCoiVvIOUP3kSzyMA,13868
6
+ s3/utils.py,sha256=NbF28CYviK_St5qd1EOumMVyus9BvQON7clUFeR_SEQ,4473
7
+ pys3uploader-0.2.4a0.dist-info/LICENSE,sha256=8k-hEraOzyum0GvmmK65YxNRTFXK7eIFHJ0OshJXeTk,1068
8
+ pys3uploader-0.2.4a0.dist-info/METADATA,sha256=LCV-hgBLudyY3q9dq5E2sg63OnapaoEYv2ggUqDsc7I,7797
9
+ pys3uploader-0.2.4a0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
10
+ pys3uploader-0.2.4a0.dist-info/top_level.txt,sha256=iQp4y1P58Q633gj8M08kHE4mqqT0hixuDWcniDk_RJ4,3
11
+ pys3uploader-0.2.4a0.dist-info/RECORD,,
s3/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from s3.uploader import Uploader # noqa: F401
2
2
 
3
- version = "0.2.2"
3
+ version = "0.2.4a0"
s3/uploader.py CHANGED
@@ -2,9 +2,10 @@ import logging
2
2
  import os
3
3
  import time
4
4
  from concurrent.futures import ThreadPoolExecutor, as_completed
5
- from typing import Dict
5
+ from typing import Dict, Iterable
6
6
 
7
7
  import boto3.resources.factory
8
+ import dotenv
8
9
  from botocore.config import Config
9
10
  from botocore.exceptions import ClientError
10
11
  from tqdm import tqdm
@@ -33,15 +34,18 @@ class Uploader:
33
34
  bucket_name: str,
34
35
  upload_dir: str,
35
36
  s3_prefix: str = None,
36
- exclude_path: str = None,
37
+ exclude_prefix: str = None,
37
38
  skip_dot_files: bool = True,
38
39
  overwrite: bool = False,
40
+ file_exclusion: Iterable[str] = None,
41
+ folder_exclusion: Iterable[str] = None,
39
42
  region_name: str = None,
40
43
  profile_name: str = None,
41
44
  aws_access_key_id: str = None,
42
45
  aws_secret_access_key: str = None,
43
46
  retry_config: Config = RETRY_CONFIG,
44
47
  logger: logging.Logger = None,
48
+ env_file: str = None,
45
49
  ):
46
50
  """Initiates all the necessary args and creates a boto3 session with retry logic.
47
51
 
@@ -49,14 +53,17 @@ class Uploader:
49
53
  bucket_name: Name of the bucket.
50
54
  upload_dir: Full path of the directory to be uploaded.
51
55
  s3_prefix: Particular bucket prefix within which the upload should happen.
52
- exclude_path: Full directory path to exclude from S3 object prefix.
56
+ exclude_prefix: Full directory path to exclude from S3 object prefix.
53
57
  skip_dot_files: Boolean flag to skip dot files.
54
58
  overwrite: Boolean flag to overwrite files in S3.
59
+ file_exclusion: Sequence of files to exclude during upload.
60
+ folder_exclusion: Sequence of directories to exclude during upload.
55
61
  region_name: Name of the AWS region.
56
62
  profile_name: AWS profile name.
57
63
  aws_access_key_id: AWS access key ID.
58
64
  aws_secret_access_key: AWS secret access key.
59
65
  logger: Bring your own logger.
66
+ env_file: Dotenv file (.env) filepath to load environment variables.
60
67
 
61
68
  See Also:
62
69
  s3_prefix:
@@ -65,29 +72,56 @@ class Uploader:
65
72
  If ``s3_prefix`` is set to: ``2025``, then the file path
66
73
  ``/home/ubuntu/Desktop/S3Upload/sub/photo.jpg`` will be uploaded as ``2025/S3Upload/sub/photo.jpg``
67
74
 
68
- exclude_path:
75
+ exclude_prefix:
69
76
  When upload directory is "/home/ubuntu/Desktop/S3Upload", each file will naturally have the full prefix.
70
- However, this behavior can be avoided by specifying the ``exclude_path`` parameter.
77
+ However, this behavior can be avoided by specifying the ``exclude_prefix`` parameter.
71
78
 
72
- If exclude_path is set to: ``/home/ubuntu/Desktop``, then the file path
79
+ If exclude_prefix is set to: ``/home/ubuntu/Desktop``, then the file path
73
80
  ``/home/ubuntu/Desktop/S3Upload/sub-dir/photo.jpg`` will be uploaded as ``S3Upload/sub-dir/photo.jpg``
81
+
82
+ env_file:
83
+ Environment variables can be loaded from a .env file.
84
+ The filepath can be set as ``env_file`` during object instantiation or as an environment variable.
85
+ If a filepath is provided, PyS3Uploader loads it directly or searches the root directory for the file.
86
+ If no filepath is provided, PyS3Uploader searches the current directory for a .env file.
74
87
  """
88
+ self.logger = logger or default_logger()
89
+ self.env_file = env_file or getenv("ENV_FILE", default=".env")
90
+
91
+ # Check for env_file in current working directory
92
+ if os.path.isfile(self.env_file):
93
+ self.logger.debug("Loading env file: %s", self.env_file)
94
+ dotenv.load_dotenv(dotenv_path=self.env_file, override=True)
95
+ # Find the env_file from root
96
+ elif env_file := dotenv.find_dotenv(self.env_file, raise_error_if_not_found=False):
97
+ self.logger.debug("Loading env file: %s", env_file)
98
+ dotenv.load_dotenv(dotenv_path=env_file, override=True)
99
+ else:
100
+ # Scan current working directory for any .env files
101
+ for file in os.listdir():
102
+ if file.endswith(".env"):
103
+ self.logger.debug("Loading env file: %s", file)
104
+ dotenv.load_dotenv(dotenv_path=file, override=True)
105
+ break
106
+ else:
107
+ self.logger.debug("No .env files found to load")
108
+
75
109
  self.session = boto3.Session(
76
- profile_name=profile_name or getenv("PROFILE_NAME"),
110
+ profile_name=profile_name or getenv("PROFILE_NAME", "AWS_PROFILE_NAME"),
77
111
  region_name=region_name or getenv("AWS_DEFAULT_REGION"),
78
112
  aws_access_key_id=aws_access_key_id or getenv("AWS_ACCESS_KEY_ID"),
79
113
  aws_secret_access_key=aws_secret_access_key or getenv("AWS_SECRET_ACCESS_KEY"),
80
114
  )
81
115
  self.s3 = self.session.resource(service_name="s3", config=retry_config)
82
116
 
83
- self.logger = logger or default_logger()
84
-
85
117
  self.bucket_name = bucket_name
86
- self.upload_dir = upload_dir or getenv("UPLOAD_DIR", "UPLOAD_SOURCE")
118
+ self.upload_dir = upload_dir
87
119
  self.s3_prefix = s3_prefix
88
- self.exclude_path = exclude_path
120
+ self.exclude_prefix = exclude_prefix
89
121
  self.skip_dot_files = skip_dot_files
90
122
  self.overwrite = overwrite
123
+ self.file_exclusion = file_exclusion or []
124
+ self.folder_exclusion = folder_exclusion or []
91
125
 
92
126
  self.results = UploadResults()
93
127
  self.start = time.time()
@@ -106,9 +140,9 @@ class Uploader:
106
140
  BucketNotFound: If bucket name was not found.
107
141
  """
108
142
  self.start = time.time()
109
- if self.exclude_path and self.exclude_path not in self.upload_dir:
143
+ if self.exclude_prefix and self.exclude_prefix not in self.upload_dir:
110
144
  raise ValueError(
111
- f"\n\n\tStart folder {self.exclude_path!r} is not a part of upload directory {self.upload_dir!r}"
145
+ f"\n\n\tStart folder {self.exclude_prefix!r} is not a part of upload directory {self.upload_dir!r}"
112
146
  )
113
147
  if not self.upload_dir:
114
148
  raise ValueError("\n\n\tCannot proceed without an upload directory.")
@@ -163,6 +197,8 @@ class Uploader:
163
197
  self.logger.info(
164
198
  "S3 object %s exists, but size mismatch. Local: [%d], S3: [%d]", objectpath, file_size, object_size
165
199
  )
200
+ else:
201
+ self.logger.debug("S3 object '%s' doesn't exist, uploading..", objectpath)
166
202
  return True
167
203
 
168
204
  def _uploader(self, filepath: str, objectpath: str) -> None:
@@ -184,13 +220,20 @@ class Uploader:
184
220
  """
185
221
  files_to_upload = {}
186
222
  for __path, __directory, __files in os.walk(self.upload_dir):
223
+ scan_dir = os.path.split(__path)[-1]
224
+ if scan_dir in self.folder_exclusion:
225
+ self.logger.info("Skipping '%s' honoring folder exclusion", scan_dir)
226
+ continue
187
227
  for file_ in __files:
228
+ if file_ in self.file_exclusion:
229
+ self.logger.info("Skipping '%s' honoring file exclusion", file_)
230
+ continue
188
231
  if self.skip_dot_files and file_.startswith("."):
189
232
  self.logger.info("Skipping dot file: %s", file_)
190
233
  continue
191
234
  file_path = os.path.join(__path, file_)
192
- if self.exclude_path:
193
- relative_path = file_path.replace(self.exclude_path, "")
235
+ if self.exclude_prefix:
236
+ relative_path = file_path.replace(self.exclude_prefix, "")
194
237
  else:
195
238
  relative_path = file_path
196
239
  # Lists in python are ordered, so s3 prefix will get loaded first when provided
@@ -213,7 +256,7 @@ class Uploader:
213
256
  self.logger.debug(keys)
214
257
  self.logger.info("%d files from '%s' will be uploaded to '%s'", len(keys), self.upload_dir, self.bucket_name)
215
258
  self.logger.info("Initiating upload process.")
216
- for objectpath, filepath in tqdm(
259
+ for filepath, objectpath in tqdm(
217
260
  keys.items(), total=len(keys), unit="file", leave=True, desc=f"Uploading files from {self.upload_dir}"
218
261
  ):
219
262
  try:
@@ -1,11 +0,0 @@
1
- s3/__init__.py,sha256=L12aFBb0plj8WISe0_He1vvQ55aOKd8VCyMlj_2LqrQ,66
2
- s3/exceptions.py,sha256=hH3jlMOe8yjBatQK9EdndWZz4QESU74KSY_iDhQ37SY,2585
3
- s3/logger.py,sha256=oH540oq8jY723jA4lDWlgfFPLbNgGXTkDwFpB7TLO_o,1196
4
- s3/tree.py,sha256=DiQ2ekMMaj2m_P3-iKkEqSuJCJZ_UZxcAwHtAoPVa5c,1824
5
- s3/uploader.py,sha256=I2An6Ix0rFMlvDLtLaDQ6F-YrN70IDCNFgh9E32cXHA,11489
6
- s3/utils.py,sha256=NbF28CYviK_St5qd1EOumMVyus9BvQON7clUFeR_SEQ,4473
7
- pys3uploader-0.2.2.dist-info/LICENSE,sha256=8k-hEraOzyum0GvmmK65YxNRTFXK7eIFHJ0OshJXeTk,1068
8
- pys3uploader-0.2.2.dist-info/METADATA,sha256=fIlyxO6dFHYH3uhFg4yZa7RtGGBKqaOynAHlrbuffoY,7449
9
- pys3uploader-0.2.2.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
10
- pys3uploader-0.2.2.dist-info/top_level.txt,sha256=iQp4y1P58Q633gj8M08kHE4mqqT0hixuDWcniDk_RJ4,3
11
- pys3uploader-0.2.2.dist-info/RECORD,,