Skip to content

Commit

Permalink
Merge branch 'release-1.26.32'
Browse files Browse the repository at this point in the history
* release-1.26.32:
  Bumping version to 1.26.32
  Add changelog entries from botocore
  S3 upload_file, download file to support path-lib objects (#2259)
  • Loading branch information
aws-sdk-python-automation committed Dec 16, 2022
2 parents dc25471 + 6af157c commit 3041f18
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 8 deletions.
42 changes: 42 additions & 0 deletions .changes/1.26.32.json
@@ -0,0 +1,42 @@
[
{
"category": "s3",
"description": "s3.transfer methods accept path-like objects as input",
"type": "enhancement"
},
{
"category": "``appflow``",
"description": "[``botocore``] This release updates the ListConnectorEntities API action so that it returns paginated responses that customers can retrieve with next tokens.",
"type": "api-change"
},
{
"category": "``cloudfront``",
"description": "[``botocore``] Updated documentation for CloudFront",
"type": "api-change"
},
{
"category": "``datasync``",
"description": "[``botocore``] AWS DataSync now supports the use of tags with task executions. With this new feature, you can apply tags each time you execute a task, giving you greater control and management over your task executions.",
"type": "api-change"
},
{
"category": "``efs``",
"description": "[``botocore``] Update efs client to latest version",
"type": "api-change"
},
{
"category": "``guardduty``",
"description": "[``botocore``] This release provides the valid characters for the Description and Name field.",
"type": "api-change"
},
{
"category": "``iotfleetwise``",
"description": "[``botocore``] Updated error handling for empty resource names in \"UpdateSignalCatalog\" and \"GetModelManifest\" operations.",
"type": "api-change"
},
{
"category": "``sagemaker``",
"description": "[``botocore``] AWS sagemaker - Features: This release adds support for random seed, it's an integer value used to initialize a pseudo-random number generator. Setting a random seed will allow the hyperparameter tuning search strategies to produce more consistent configurations for the same tuning job.",
"type": "api-change"
}
]
13 changes: 13 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -2,6 +2,19 @@
CHANGELOG
=========

1.26.32
=======

* enhancement:s3: s3.transfer methods accept path-like objects as input
* api-change:``appflow``: [``botocore``] This release updates the ListConnectorEntities API action so that it returns paginated responses that customers can retrieve with next tokens.
* api-change:``cloudfront``: [``botocore``] Updated documentation for CloudFront
* api-change:``datasync``: [``botocore``] AWS DataSync now supports the use of tags with task executions. With this new feature, you can apply tags each time you execute a task, giving you greater control and management over your task executions.
* api-change:``efs``: [``botocore``] Update efs client to latest version
* api-change:``guardduty``: [``botocore``] This release provides the valid characters for the Description and Name field.
* api-change:``iotfleetwise``: [``botocore``] Updated error handling for empty resource names in "UpdateSignalCatalog" and "GetModelManifest" operations.
* api-change:``sagemaker``: [``botocore``] AWS sagemaker - Features: This release adds support for random seed, it's an integer value used to initialize a pseudo-random number generator. Setting a random seed will allow the hyperparameter tuning search strategies to produce more consistent configurations for the same tuning job.


1.26.31
=======

Expand Down
2 changes: 1 addition & 1 deletion boto3/__init__.py
Expand Up @@ -17,7 +17,7 @@
from boto3.session import Session

__author__ = 'Amazon Web Services'
__version__ = '1.26.31'
__version__ = '1.26.32'


# The default Boto3 session; autoloaded when needed.
Expand Down
10 changes: 8 additions & 2 deletions boto3/s3/transfer.py
Expand Up @@ -122,6 +122,8 @@ def __call__(self, bytes_amount):
"""
from os import PathLike, fspath

from botocore.exceptions import ClientError
from s3transfer.exceptions import (
RetriesExceededError as S3TransferRetriesExceededError,
Expand Down Expand Up @@ -277,8 +279,10 @@ def upload_file(
:py:meth:`S3.Client.upload_file`
:py:meth:`S3.Client.upload_fileobj`
"""
if isinstance(filename, PathLike):
filename = fspath(filename)
if not isinstance(filename, str):
raise ValueError('Filename must be a string')
raise ValueError('Filename must be a string or a path-like object')

subscribers = self._get_subscribers(callback)
future = self._manager.upload(
Expand Down Expand Up @@ -309,8 +313,10 @@ def download_file(
:py:meth:`S3.Client.download_file`
:py:meth:`S3.Client.download_fileobj`
"""
if isinstance(filename, PathLike):
filename = fspath(filename)
if not isinstance(filename, str):
raise ValueError('Filename must be a string')
raise ValueError('Filename must be a string or a path-like object')

subscribers = self._get_subscribers(callback)
future = self._manager.download(
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Expand Up @@ -3,7 +3,7 @@ universal = 0

[metadata]
requires_dist =
botocore>=1.29.31,<1.30.0
botocore>=1.29.32,<1.30.0
jmespath>=0.7.1,<2.0.0
s3transfer>=0.6.0,<0.7.0

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -13,7 +13,7 @@


requires = [
'botocore>=1.29.31,<1.30.0',
'botocore>=1.29.32,<1.30.0',
'jmespath>=0.7.1,<2.0.0',
's3transfer>=0.6.0,<0.7.0',
]
Expand Down
9 changes: 8 additions & 1 deletion tests/integration/test_s3.py
Expand Up @@ -20,6 +20,7 @@
import string
import tempfile
import threading
from pathlib import Path

from botocore.client import Config

Expand Down Expand Up @@ -386,6 +387,13 @@ def test_download_fileobj(self):

self.assertEqual(fileobj.getvalue(), b'beach')

def test_upload_via_path(self):
transfer = self.create_s3_transfer()
filename = self.files.create_file_with_size('path.txt', filesize=1024)
transfer.upload_file(Path(filename), self.bucket_name, 'path.txt')
self.addCleanup(self.delete_object, 'path.txt')
self.assertTrue(self.object_exists('path.txt'))

def test_upload_below_threshold(self):
config = boto3.s3.transfer.TransferConfig(
multipart_threshold=2 * 1024 * 1024
Expand All @@ -396,7 +404,6 @@ def test_upload_below_threshold(self):
)
transfer.upload_file(filename, self.bucket_name, 'foo.txt')
self.addCleanup(self.delete_object, 'foo.txt')

self.assertTrue(self.object_exists('foo.txt'))

def test_upload_above_threshold(self):
Expand Down
55 changes: 53 additions & 2 deletions tests/unit/s3/test_transfer.py
Expand Up @@ -10,6 +10,9 @@
# distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import pathlib
from tempfile import NamedTemporaryFile

import pytest
from s3transfer.futures import NonThreadedExecutor
from s3transfer.manager import TransferManager
Expand Down Expand Up @@ -126,6 +129,11 @@ def setUp(self):
self.manager = mock.Mock(TransferManager(self.client))
self.transfer = S3Transfer(manager=self.manager)
self.callback = mock.Mock()
# Use NamedTempFile as source of a path string that is valid and
# realistic for the system the tests are run on. The file gets deleted
# immediately and will not actually exist while the tests are run.
with NamedTemporaryFile("w") as tmp_file:
self.file_path_str = tmp_file.name

def assert_callback_wrapped_in_subscriber(self, call_args):
subscribers = call_args[0][4]
Expand All @@ -148,16 +156,59 @@ def test_upload_file(self):
'smallfile', 'bucket', 'key', extra_args, None
)

def test_upload_file_via_path(self):
extra_args = {'ACL': 'public-read'}
self.transfer.upload_file(
pathlib.Path(self.file_path_str),
'bucket',
'key',
extra_args=extra_args,
)
self.manager.upload.assert_called_with(
self.file_path_str, 'bucket', 'key', extra_args, None
)

def test_upload_file_via_purepath(self):
extra_args = {'ACL': 'public-read'}
self.transfer.upload_file(
pathlib.PurePath(self.file_path_str),
'bucket',
'key',
extra_args=extra_args,
)
self.manager.upload.assert_called_with(
self.file_path_str, 'bucket', 'key', extra_args, None
)

def test_download_file(self):
extra_args = {
'SSECustomerKey': 'foo',
'SSECustomerAlgorithm': 'AES256',
}
self.transfer.download_file(
'bucket', 'key', '/tmp/smallfile', extra_args=extra_args
'bucket', 'key', self.file_path_str, extra_args=extra_args
)
self.manager.download.assert_called_with(
'bucket', 'key', self.file_path_str, extra_args, None
)

def test_download_file_via_path(self):
extra_args = {
'SSECustomerKey': 'foo',
'SSECustomerAlgorithm': 'AES256',
}
self.transfer.download_file(
'bucket',
'key',
pathlib.Path(self.file_path_str),
extra_args=extra_args,
)
self.manager.download.assert_called_with(
'bucket', 'key', '/tmp/smallfile', extra_args, None
'bucket',
'key',
self.file_path_str,
extra_args,
None,
)

def test_upload_wraps_callback(self):
Expand Down

0 comments on commit 3041f18

Please sign in to comment.