Skip to content

Commit

Permalink
remove utcnow() calls to fix deprecation warnings encountered in py…
Browse files Browse the repository at this point in the history
…thon 3.12
  • Loading branch information
davidlm committed Dec 5, 2023
1 parent a1bc821 commit 924c01b
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 95 deletions.
6 changes: 3 additions & 3 deletions botocore/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ def signature(self, string_to_sign, request):
def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError()
datetime_now = datetime.datetime.utcnow()
datetime_now = datetime.datetime.now(datetime.timezone.utc)
request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)
# This could be a retry. Make sure the previous
# authorization header is removed first.
Expand Down Expand Up @@ -554,7 +554,7 @@ class S3ExpressPostAuth(S3ExpressAuth):
REQUIRES_IDENTITY_CACHE = True

def add_auth(self, request):
datetime_now = datetime.datetime.utcnow()
datetime_now = datetime.datetime.now(datetime.timezone.utc)
request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)

fields = {}
Expand Down Expand Up @@ -813,7 +813,7 @@ class S3SigV4PostAuth(SigV4Auth):
"""

def add_auth(self, request):
datetime_now = datetime.datetime.utcnow()
datetime_now = datetime.datetime.now(datetime.timezone.utc)
request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)

fields = {}
Expand Down
12 changes: 2 additions & 10 deletions botocore/crt/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError()

# Use utcnow() because that's what gets mocked by tests, but set
# timezone because CRT assumes naive datetime is local time.
datetime_now = datetime.datetime.utcnow().replace(
tzinfo=datetime.timezone.utc
)
datetime_now = datetime.datetime.now(datetime.timezone.utc)

# Use existing 'X-Amz-Content-SHA256' header if able
existing_sha256 = self._get_existing_sha256(request)
Expand Down Expand Up @@ -251,11 +247,7 @@ def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError()

# Use utcnow() because that's what gets mocked by tests, but set
# timezone because CRT assumes naive datetime is local time.
datetime_now = datetime.datetime.utcnow().replace(
tzinfo=datetime.timezone.utc
)
datetime_now = datetime.datetime.now(datetime.timezone.utc)

# Use existing 'X-Amz-Content-SHA256' header if able
existing_sha256 = self._get_existing_sha256(request)
Expand Down
9 changes: 6 additions & 3 deletions botocore/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,24 +152,27 @@ def prepare_request(self, request):
def _calculate_ttl(
self, response_received_timestamp, date_header, read_timeout
):
local_timestamp = datetime.datetime.utcnow()
local_timestamp = datetime.datetime.now(datetime.timezone.utc)
date_conversion = datetime.datetime.strptime(
date_header, "%a, %d %b %Y %H:%M:%S %Z"
)
).replace(tzinfo=datetime.timezone.utc)
estimated_skew = date_conversion - response_received_timestamp
ttl = (
local_timestamp
+ datetime.timedelta(seconds=read_timeout)
+ estimated_skew
)

return ttl.strftime('%Y%m%dT%H%M%SZ')

def _set_ttl(self, retries_context, read_timeout, success_response):
response_date_header = success_response[0].headers.get('Date')
has_streaming_input = retries_context.get('has_streaming_input')
if response_date_header and not has_streaming_input:
try:
response_received_timestamp = datetime.datetime.utcnow()
response_received_timestamp = datetime.datetime.now(
datetime.timezone.utc
)
retries_context['ttl'] = self._calculate_ttl(
response_received_timestamp,
response_date_header,
Expand Down
2 changes: 1 addition & 1 deletion botocore/signers.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ def generate_presigned_post(
policy = {}

# Create an expiration date for the policy
datetime_now = datetime.datetime.utcnow()
datetime_now = datetime.datetime.now(datetime.timezone.utc)
expire_date = datetime_now + datetime.timedelta(seconds=expires_in)
policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601)

Expand Down
21 changes: 20 additions & 1 deletion botocore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@
flags=re.IGNORECASE,
)

ISO_FORMAT = '%Y-%m-%dT%H:%M:%S'
ISO_FORMAT_MICRO = f'{ISO_FORMAT}.%f'


def ensure_boolean(val):
"""Ensures a boolean value if a string or boolean is provided
Expand Down Expand Up @@ -673,7 +676,7 @@ def _evaluate_expiration(self, credentials):
)
jitter = random.randint(120, 600) # Between 2 to 10 minutes
refresh_interval_with_jitter = refresh_interval + jitter
current_time = datetime.datetime.utcnow()
current_time = datetime.datetime.now(datetime.timezone.utc)
refresh_offset = datetime.timedelta(
seconds=refresh_interval_with_jitter
)
Expand Down Expand Up @@ -1070,6 +1073,22 @@ def datetime2timestamp(dt, default_timezone=None):
) / 10**6


def isoformat(dt):
"""Implementing datetime.isoformat() without timezone awareness. This is
necessary because datetime.utcnow() has been deprecated as of Python
3.12. Timezone aware objects are now being used more in the SDK so to
maintain backwards compatibility this function is used to ensure that
the timezone offset e.g. +00:00 is not included in the timestamp.
:type datetime: datetime
:param datetime: A datetime object to be converted into timestamp
:returns: The timestamp
"""
if dt.microsecond:
return datetime.strftime(dt, ISO_FORMAT_MICRO)
return datetime.strftime(dt, ISO_FORMAT)


def calculate_sha256(body, as_hex=False):
"""Calculate a sha256 checksum.
Expand Down
6 changes: 3 additions & 3 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,20 +568,20 @@ class FreezeTime(ContextDecorator):
:param module: reference to imported module to patch (e.g. botocore.auth.datetime)
:type date: datetime.datetime
:param date: datetime object specifying the output for utcnow()
:param date: datetime object specifying the output for now()
"""

def __init__(self, module, date=None):
if date is None:
date = datetime.datetime.utcnow()
date = datetime.datetime.now(datetime.timezone.utc)
self.date = date
self.datetime_patcher = mock.patch.object(
module, 'datetime', mock.Mock(wraps=datetime.datetime)
)

def __enter__(self, *args, **kwargs):
mock = self.datetime_patcher.start()
mock.utcnow.return_value = self.date
mock.now.return_value = self.date

def __exit__(self, *args, **kwargs):
self.datetime_patcher.stop()
Expand Down
6 changes: 4 additions & 2 deletions tests/functional/test_ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,16 @@ def setUp(self):
'<snapshotId>%s</snapshotId>\n'
'</CopySnapshotResponse>\n'
)
self.now = datetime.datetime(2011, 9, 9, 23, 36)
self.now = datetime.datetime(
2011, 9, 9, 23, 36, tzinfo=datetime.timezone.utc
)
self.datetime_patch = mock.patch.object(
botocore.auth.datetime,
'datetime',
mock.Mock(wraps=datetime.datetime),
)
self.mocked_datetime = self.datetime_patch.start()
self.mocked_datetime.utcnow.return_value = self.now
self.mocked_datetime.now.return_value = self.now

def tearDown(self):
super().tearDown()
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/test_lex.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_unsigned_payload(self):
timestamp = datetime(2017, 3, 22, 0, 0)

with mock.patch('botocore.auth.datetime.datetime') as _datetime:
_datetime.utcnow.return_value = timestamp
_datetime.now.return_value = timestamp
self.http_stubber.add_response(body=b'{}')
with self.http_stubber:
self.client.post_content(**params)
Expand Down
72 changes: 36 additions & 36 deletions tests/functional/test_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,27 @@ def _retry_headers_test_cases(self):
]

# The first, third and seventh datetime values of each
# utcnow_side_effects list are side_effect values for when
# utcnow is called in SigV4 signing.
utcnow_side_effects = [
# dt_now_side_effects list are side_effect values for when
# datetime.now is called in SigV4 signing.
utc = datetime.timezone.utc
dt_now_side_effects = [
[
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 1, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 1, 0),
datetime.datetime(2019, 6, 1, 0, 0, 2, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 1, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 1, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 2, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
],
[
datetime.datetime(2020, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 5, 0),
datetime.datetime(2019, 6, 1, 0, 0, 6, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2019, 6, 1, 0, 0, 11, 0),
datetime.datetime(2019, 6, 1, 0, 0, 12, 0),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0),
datetime.datetime(2020, 6, 1, 0, 0, 0, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 5, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 6, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 11, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 12, 0, tzinfo=utc),
datetime.datetime(2019, 6, 1, 0, 0, 0, 0, tzinfo=utc),
],
]
expected_headers = [
Expand All @@ -101,36 +102,35 @@ def _retry_headers_test_cases(self):
],
]
test_cases = list(
zip(responses, utcnow_side_effects, expected_headers)
zip(responses, dt_now_side_effects, expected_headers)
)
return test_cases

def _test_amz_sdk_request_header_with_test_case(
self, responses, utcnow_side_effects, expected_headers, client_config
self, responses, dt_now_side_effects, expected_headers, client_config
):
datetime_patcher = mock.patch.object(
botocore.endpoint.datetime,
'datetime',
mock.Mock(wraps=datetime.datetime),
)
mocked_datetime = datetime_patcher.start()
mocked_datetime.utcnow.side_effect = utcnow_side_effects

client = self.session.create_client(
'dynamodb', self.region, config=client_config
)
with ClientHTTPStubber(client) as http_stubber:
for response in responses:
http_stubber.add_response(
headers=response[1], status=response[0], body=b'{}'
)
client.list_tables()
amz_sdk_request_headers = [
request.headers['amz-sdk-request']
for request in http_stubber.requests
]
self.assertListEqual(amz_sdk_request_headers, expected_headers)
datetime_patcher.stop()
with datetime_patcher as mock_datetime:
mock_datetime.now.side_effect = dt_now_side_effects
client = self.session.create_client(
'dynamodb', self.region, config=client_config
)
with ClientHTTPStubber(client) as http_stubber:
for response in responses:
http_stubber.add_response(
headers=response[1], status=response[0], body=b'{}'
)
client.list_tables()
amz_sdk_request_headers = [
request.headers['amz-sdk-request']
for request in http_stubber.requests
]
self.assertListEqual(amz_sdk_request_headers, expected_headers)

def test_amz_sdk_request_header(self):
test_cases = self._retry_headers_test_cases()
Expand Down
8 changes: 1 addition & 7 deletions tests/functional/test_s3express.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ def test_s3_express_auth_headers(self):
class TestS3ExpressIdentityCache:
def test_default_s3_express_cache(self, default_s3_client, mock_datetime):
mock_datetime.now.return_value = DATE
mock_datetime.utcnow.return_value = DATE

identity_cache = S3ExpressIdentityCache(
default_s3_client,
Expand All @@ -126,7 +125,6 @@ def test_s3_express_cache_one_network_call(
self, default_s3_client, mock_datetime
):
mock_datetime.now.return_value = DATE
mock_datetime.utcnow.return_value = DATE
bucket = 'my_bucket'

identity_cache = S3ExpressIdentityCache(
Expand All @@ -151,7 +149,6 @@ def test_s3_express_cache_multiple_buckets(
self, default_s3_client, mock_datetime
):
mock_datetime.now.return_value = DATE
mock_datetime.utcnow.return_value = DATE
bucket = 'my_bucket'
other_bucket = 'other_bucket'

Expand Down Expand Up @@ -204,7 +201,7 @@ def _call_get_object(self, client):
)

def test_create_bucket(self, default_s3_client, mock_datetime):
mock_datetime.utcnow.return_value = DATE
mock_datetime.now.return_value = DATE

with ClientHTTPStubber(default_s3_client) as stubber:
stubber.add_response()
Expand All @@ -228,7 +225,6 @@ def test_create_bucket(self, default_s3_client, mock_datetime):
self._assert_standard_sigv4_signature(stubber.requests[0].headers)

def test_get_object(self, default_s3_client, mock_datetime):
mock_datetime.utcnow.return_value = DATE
mock_datetime.now.return_value = DATE

with ClientHTTPStubber(default_s3_client) as stubber:
Expand All @@ -250,7 +246,6 @@ def test_get_object(self, default_s3_client, mock_datetime):
def test_cache_with_multiple_requests(
self, default_s3_client, mock_datetime
):
mock_datetime.utcnow.return_value = DATE
mock_datetime.now.return_value = DATE

with ClientHTTPStubber(default_s3_client) as stubber:
Expand All @@ -275,7 +270,6 @@ def test_cache_with_multiple_requests(
def test_delete_objects_injects_correct_checksum(
self, default_s3_client, mock_datetime
):
mock_datetime.utcnow.return_value = DATE
mock_datetime.now.return_value = DATE

with ClientHTTPStubber(default_s3_client) as stubber:
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/test_sts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import re
from datetime import datetime
from datetime import datetime, timezone

from botocore.config import Config
from botocore.stub import Stubber
Expand All @@ -37,9 +37,9 @@ def setUp(self):
self.stubber.activate()

def test_presigned_url_contains_no_content_type(self):
timestamp = datetime(2017, 3, 22, 0, 0)
timestamp = datetime(2017, 3, 22, 0, 0, tzinfo=timezone.utc)
with mock.patch('botocore.auth.datetime.datetime') as _datetime:
_datetime.utcnow.return_value = timestamp
_datetime.now.return_value = timestamp
url = self.client.generate_presigned_url('get_caller_identity', {})

# There should be no 'content-type' in x-amz-signedheaders
Expand Down
Loading

0 comments on commit 924c01b

Please sign in to comment.