Skip to content

Commit db757c7

Browse files
authored
Bump botocore dependency specification (#1534)
1 parent adb140a commit db757c7

9 files changed

Lines changed: 356 additions & 17 deletions

File tree

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
Changes
22
-------
33

4+
3.4.0 (2026-04-07)
5+
^^^^^^^^^^^^^^^^^^
6+
* bump botocore dependency specification to support ``"botocore >= 1.42.79, < 1.42.85"``
7+
48
3.3.0 (2026-03-18)
59
^^^^^^^^^^^^^^^^^^
610
* bump botocore dependency specification to support ``"botocore >= 1.42.62, < 1.42.71"``

aiobotocore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.3.0'
1+
__version__ = '3.4.0'

aiobotocore/args.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ async def get_client_args(
5151
signing_region = endpoint_config['signing_region']
5252
endpoint_region_name = endpoint_config['region_name']
5353
account_id_endpoint_mode = config_kwargs['account_id_endpoint_mode']
54+
s3_disable_express_session_auth = config_kwargs[
55+
's3_disable_express_session_auth'
56+
]
5457

5558
event_emitter = copy.copy(self._event_emitter)
5659
signer = AioRequestSigner(
@@ -125,6 +128,7 @@ async def get_client_args(
125128
event_emitter,
126129
credentials,
127130
account_id_endpoint_mode,
131+
s3_disable_express_session_auth,
128132
)
129133

130134
# Copy the session's user agent factory and adds client configuration.
@@ -164,6 +168,7 @@ def _build_endpoint_resolver(
164168
event_emitter,
165169
credentials,
166170
account_id_endpoint_mode,
171+
s3_disable_express_session_auth,
167172
):
168173
if endpoints_ruleset_data is None:
169174
return None
@@ -190,6 +195,7 @@ def _build_endpoint_resolver(
190195
legacy_endpoint_url=endpoint.host,
191196
credentials=credentials,
192197
account_id_endpoint_mode=account_id_endpoint_mode,
198+
s3_disable_express_session_auth=s3_disable_express_session_auth,
193199
)
194200

195201
# replace with async version
@@ -206,6 +212,10 @@ def _build_endpoint_resolver(
206212
client_context = {}
207213
if self._is_s3_service(service_name_raw):
208214
client_context.update(s3_config_raw)
215+
if s3_disable_express_session_auth is not None:
216+
client_context['disable_s3_express_session_auth'] = (
217+
s3_disable_express_session_auth
218+
)
209219

210220
sig_version = (
211221
client_config.signature_version

aiobotocore/httpchecksum.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,22 @@ def _apply_request_trailer_checksum(request):
280280
headers["X-Amz-Trailer"] = location_name
281281

282282
content_length = determine_content_length(body)
283+
if content_length is None and "Content-Length" in headers:
284+
# determine_content_length() cannot resolve the length of non-seekable
285+
# bodies, but the caller may have set Content-Length explicitly. Reuse
286+
# that value for X-Amz-Decoded-Content-Length before the header is
287+
# removed for chunked transfer encoding.
288+
content_length = int(headers["Content-Length"])
283289
if content_length is not None:
284290
# Send the decoded content length if we can determine it. Some
285291
# services such as S3 may require the decoded content length
286292
headers["X-Amz-Decoded-Content-Length"] = str(content_length)
287293

288-
if "Content-Length" in headers:
289-
del headers["Content-Length"]
290-
logger.debug(
291-
"Removing the Content-Length header since 'chunked' is specified for Transfer-Encoding."
292-
)
294+
if "Content-Length" in headers:
295+
del headers["Content-Length"]
296+
logger.debug(
297+
"Removing the Content-Length header since 'chunked' is specified for Transfer-Encoding."
298+
)
293299

294300
if isinstance(body, (bytes, bytearray)):
295301
body = io.BytesIO(body)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dynamic = ["version", "readme"]
3131
dependencies = [
3232
"aiohttp >= 3.12.0, < 4.0.0",
3333
"aioitertools >= 0.5.1, < 1.0.0",
34-
"botocore >= 1.42.62, < 1.42.71",
34+
"botocore >= 1.42.79, < 1.42.85",
3535
"python-dateutil >= 2.1, < 3.0.0",
3636
"jmespath >= 0.7.1, < 2.0.0",
3737
"multidict >= 6.0.0, < 7.0.0",
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
#!/usr/bin/env
2+
# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"). You
5+
# may not use this file except in compliance with the License. A copy of
6+
# the License is located at
7+
#
8+
# http://aws.amazon.com/apache2.0/
9+
#
10+
# or in the "license" file accompanying this file. This file is
11+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12+
# ANY KIND, either express or implied. See the License for the specific
13+
# language governing permissions and limitations under the License.
14+
from botocore.client import ClientEndpointBridge
15+
from botocore.configprovider import ConfigValueStore
16+
from botocore.useragent import UserAgentString
17+
18+
from aiobotocore import args
19+
from aiobotocore.config import AioConfig
20+
from aiobotocore.credentials import AioCredentials
21+
from aiobotocore.hooks import AioHierarchicalEmitter
22+
from tests.botocore_tests import mock, unittest
23+
24+
25+
class TestEndpointResolverBuiltins(unittest.TestCase):
26+
def setUp(self):
27+
event_emitter = mock.Mock(AioHierarchicalEmitter)
28+
self.config_store = ConfigValueStore()
29+
user_agent_creator = UserAgentString(
30+
platform_name=None,
31+
platform_version=None,
32+
platform_machine=None,
33+
python_version=None,
34+
python_implementation=None,
35+
execution_env=None,
36+
crt_version=None,
37+
)
38+
self.args_create = args.AioClientArgsCreator(
39+
event_emitter=event_emitter,
40+
user_agent=None,
41+
response_parser_factory=None,
42+
loader=None,
43+
exceptions_factory=None,
44+
config_store=self.config_store,
45+
user_agent_creator=user_agent_creator,
46+
)
47+
self.bridge = ClientEndpointBridge(
48+
endpoint_resolver=mock.Mock(),
49+
scoped_config=None,
50+
client_config=AioConfig(),
51+
default_endpoint=None,
52+
service_signing_name=None,
53+
config_store=self.config_store,
54+
)
55+
# assume a legacy endpoint resolver that uses the builtin
56+
# endpoints.json file
57+
self.bridge.endpoint_resolver.uses_builtin_data = True
58+
59+
def call_compute_endpoint_resolver_builtin_defaults(self, **overrides):
60+
defaults = {
61+
'region_name': 'ca-central-1',
62+
'service_name': 'fooservice',
63+
's3_disable_express_session_auth': False,
64+
's3_config': {},
65+
'endpoint_bridge': self.bridge,
66+
'client_endpoint_url': None,
67+
'legacy_endpoint_url': 'https://my.legacy.endpoint.com',
68+
'credentials': None,
69+
'account_id_endpoint_mode': 'preferred',
70+
}
71+
kwargs = {**defaults, **overrides}
72+
return self.args_create.compute_endpoint_resolver_builtin_defaults(
73+
**kwargs
74+
)
75+
76+
def test_builtins_defaults(self):
77+
bins = self.call_compute_endpoint_resolver_builtin_defaults()
78+
self.assertEqual(bins['AWS::Region'], 'ca-central-1')
79+
self.assertEqual(bins['AWS::UseFIPS'], False)
80+
self.assertEqual(bins['AWS::UseDualStack'], False)
81+
self.assertEqual(bins['AWS::STS::UseGlobalEndpoint'], False)
82+
self.assertEqual(bins['AWS::S3::UseGlobalEndpoint'], False)
83+
self.assertEqual(bins['AWS::S3::Accelerate'], False)
84+
self.assertEqual(bins['AWS::S3::ForcePathStyle'], False)
85+
self.assertEqual(bins['AWS::S3::UseArnRegion'], True)
86+
self.assertEqual(bins['AWS::S3Control::UseArnRegion'], False)
87+
self.assertEqual(
88+
bins['AWS::S3::DisableMultiRegionAccessPoints'], False
89+
)
90+
self.assertEqual(bins['AWS::S3::DisableS3ExpressSessionAuth'], False)
91+
self.assertEqual(bins['SDK::Endpoint'], None)
92+
self.assertEqual(bins['AWS::Auth::AccountId'], None)
93+
self.assertEqual(bins['AWS::Auth::AccountIdEndpointMode'], 'preferred')
94+
95+
def test_aws_region(self):
96+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
97+
region_name='my-region-1',
98+
)
99+
self.assertEqual(bins['AWS::Region'], 'my-region-1')
100+
101+
def test_aws_use_fips_when_config_is_set_true(self):
102+
self.config_store.set_config_variable('use_fips_endpoint', True)
103+
bins = self.call_compute_endpoint_resolver_builtin_defaults()
104+
self.assertEqual(bins['AWS::UseFIPS'], True)
105+
106+
def test_aws_use_fips_when_config_is_set_false(self):
107+
self.config_store.set_config_variable('use_fips_endpoint', False)
108+
bins = self.call_compute_endpoint_resolver_builtin_defaults()
109+
self.assertEqual(bins['AWS::UseFIPS'], False)
110+
111+
def test_aws_use_dualstack_when_config_is_set_true(self):
112+
self.bridge.client_config = AioConfig(
113+
s3={'use_dualstack_endpoint': True}
114+
)
115+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
116+
service_name='s3-control'
117+
)
118+
self.assertEqual(bins['AWS::UseDualStack'], True)
119+
120+
def test_aws_use_dualstack_when_config_is_set_false(self):
121+
self.bridge.client_config = AioConfig(
122+
s3={'use_dualstack_endpoint': False}
123+
)
124+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
125+
service_name='s3-control'
126+
)
127+
self.assertEqual(bins['AWS::UseDualStack'], False)
128+
129+
def test_aws_use_dualstack_when_non_dualstack_service(self):
130+
self.bridge.client_config = AioConfig(
131+
s3={'use_dualstack_endpoint': True}
132+
)
133+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
134+
service_name='other-service'
135+
)
136+
self.assertEqual(bins['AWS::UseDualStack'], False)
137+
138+
def test_aws_sts_global_endpoint_with_default_and_legacy_region(self):
139+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
140+
region_name='us-west-2',
141+
)
142+
self.assertEqual(bins['AWS::STS::UseGlobalEndpoint'], False)
143+
144+
def test_aws_sts_global_endpoint_with_default_and_nonlegacy_region(self):
145+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
146+
region_name='eu-south-1',
147+
)
148+
self.assertEqual(bins['AWS::STS::UseGlobalEndpoint'], False)
149+
150+
def test_aws_sts_global_endpoint_with_nondefault_config(self):
151+
self.config_store.set_config_variable(
152+
'sts_regional_endpoints', 'regional'
153+
)
154+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
155+
region_name='us-west-2',
156+
)
157+
self.assertEqual(bins['AWS::STS::UseGlobalEndpoint'], False)
158+
159+
def test_s3_global_endpoint(self):
160+
# The only reason for this builtin to not have the default value
161+
# (False) is that the ``_should_force_s3_global`` method
162+
# returns True.
163+
self.args_create._should_force_s3_global = mock.Mock(return_value=True)
164+
bins = self.call_compute_endpoint_resolver_builtin_defaults()
165+
self.assertTrue(bins['AWS::S3::UseGlobalEndpoint'])
166+
self.args_create._should_force_s3_global.assert_called_once()
167+
168+
def test_s3_accelerate_with_config_set_true(self):
169+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
170+
s3_config={'use_accelerate_endpoint': True},
171+
)
172+
self.assertEqual(bins['AWS::S3::Accelerate'], True)
173+
174+
def test_s3_accelerate_with_config_set_false(self):
175+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
176+
s3_config={'use_accelerate_endpoint': False},
177+
)
178+
self.assertEqual(bins['AWS::S3::Accelerate'], False)
179+
180+
def test_force_path_style_with_config_set_to_path(self):
181+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
182+
s3_config={'addressing_style': 'path'},
183+
)
184+
self.assertEqual(bins['AWS::S3::ForcePathStyle'], True)
185+
186+
def test_force_path_style_with_config_set_to_auto(self):
187+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
188+
s3_config={'addressing_style': 'auto'},
189+
)
190+
self.assertEqual(bins['AWS::S3::ForcePathStyle'], False)
191+
192+
def test_force_path_style_with_config_set_to_virtual(self):
193+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
194+
s3_config={'addressing_style': 'virtual'},
195+
)
196+
self.assertEqual(bins['AWS::S3::ForcePathStyle'], False)
197+
198+
def test_use_arn_region_with_config_set_false(self):
199+
# These two builtins both take their value from the ``use_arn_region``
200+
# in the S3 configuration, but have different default values.
201+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
202+
s3_config={'use_arn_region': False},
203+
)
204+
self.assertEqual(bins['AWS::S3::UseArnRegion'], False)
205+
self.assertEqual(bins['AWS::S3Control::UseArnRegion'], False)
206+
207+
def test_use_arn_region_with_config_set_true(self):
208+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
209+
s3_config={'use_arn_region': True},
210+
)
211+
self.assertEqual(bins['AWS::S3::UseArnRegion'], True)
212+
self.assertEqual(bins['AWS::S3Control::UseArnRegion'], True)
213+
214+
def test_disable_mrap_with_config_set_true(self):
215+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
216+
s3_config={'s3_disable_multiregion_access_points': True},
217+
)
218+
self.assertEqual(bins['AWS::S3::DisableMultiRegionAccessPoints'], True)
219+
220+
def test_disable_mrap_with_config_set_false(self):
221+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
222+
s3_config={'s3_disable_multiregion_access_points': False},
223+
)
224+
self.assertEqual(
225+
bins['AWS::S3::DisableMultiRegionAccessPoints'], False
226+
)
227+
228+
def test_sdk_endpoint_both_inputs_set(self):
229+
# assume a legacy endpoint resolver that uses a customized
230+
# endpoints.json file
231+
self.bridge.endpoint_resolver.uses_builtin_data = False
232+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
233+
client_endpoint_url='https://my.client.endpoint.com',
234+
legacy_endpoint_url='https://my.legacy.endpoint.com',
235+
)
236+
self.assertEqual(
237+
bins['SDK::Endpoint'], 'https://my.client.endpoint.com'
238+
)
239+
240+
def test_sdk_endpoint_legacy_set_with_builtin_data(self):
241+
# assume a legacy endpoint resolver that uses a customized
242+
# endpoints.json file
243+
self.bridge.endpoint_resolver.uses_builtin_data = False
244+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
245+
client_endpoint_url=None,
246+
legacy_endpoint_url='https://my.legacy.endpoint.com',
247+
)
248+
self.assertEqual(
249+
bins['SDK::Endpoint'], 'https://my.legacy.endpoint.com'
250+
)
251+
252+
def test_sdk_endpoint_legacy_set_without_builtin_data(self):
253+
# assume a legacy endpoint resolver that uses the builtin
254+
# endpoints.json file
255+
self.bridge.endpoint_resolver.uses_builtin_data = True
256+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
257+
client_endpoint_url=None,
258+
legacy_endpoint_url='https://my.legacy.endpoint.com',
259+
)
260+
self.assertEqual(bins['SDK::Endpoint'], None)
261+
262+
def test_account_id_set_with_credentials(self):
263+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
264+
credentials=AioCredentials(
265+
access_key='foo', secret_key='bar', account_id='baz'
266+
)
267+
)
268+
self.assertEqual(bins['AWS::Auth::AccountId'](), 'baz')
269+
270+
def test_account_id_endpoint_mode_set_to_disabled(self):
271+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
272+
account_id_endpoint_mode='disabled'
273+
)
274+
self.assertEqual(bins['AWS::Auth::AccountIdEndpointMode'], 'disabled')
275+
276+
def test_disable_s3_express_session_auth_default(self):
277+
bins = self.call_compute_endpoint_resolver_builtin_defaults()
278+
self.assertEqual(bins['AWS::S3::DisableS3ExpressSessionAuth'], False)
279+
280+
def test_disable_s3_express_session_auth_set_to_false(self):
281+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
282+
s3_disable_express_session_auth=False,
283+
)
284+
self.assertEqual(bins['AWS::S3::DisableS3ExpressSessionAuth'], False)
285+
286+
def test_disable_s3_express_session_auth_set_to_true(self):
287+
bins = self.call_compute_endpoint_resolver_builtin_defaults(
288+
s3_disable_express_session_auth=True,
289+
)
290+
self.assertEqual(bins['AWS::S3::DisableS3ExpressSessionAuth'], True)

0 commit comments

Comments
 (0)