Skip to content

Commit d6a872d

Browse files
Bump botocore dependency specification (#1389)
1 parent ea8a695 commit d6a872d

File tree

10 files changed

+527
-17
lines changed

10 files changed

+527
-17
lines changed

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+
2.24.0 (2025-07-31)
5+
^^^^^^^^^^^^^^^^^^^
6+
* bump botocore dependency specification
7+
48
2.23.2 (2025-07-24)
59
^^^^^^^^^^^^^^^^^^^
610
* bump botocore dependency specification

aiobotocore/__init__.py

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

aiobotocore/credentials.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
SSOTokenLoader,
4343
UnauthorizedSSOTokenError,
4444
UnknownCredentialError,
45-
_get_client_creator,
4645
_local_now,
4746
_parse_if_needed,
4847
_serialize_if_needed,
@@ -56,6 +55,7 @@
5655
from aiobotocore.utils import (
5756
AioContainerMetadataFetcher,
5857
AioInstanceMetadataFetcher,
58+
create_nested_client,
5959
)
6060

6161
logger = logging.getLogger(__name__)
@@ -1101,3 +1101,20 @@ async def load(self):
11011101
method=self.METHOD,
11021102
refresh_using=sso_fetcher.fetch_credentials,
11031103
)
1104+
1105+
1106+
def _get_client_creator(session, region_name):
1107+
"""Create a client creator function for use in credential providers.
1108+
1109+
This is the async version of botocore.credentials._get_client_creator that
1110+
uses aiobotocore's create_nested_client.
1111+
"""
1112+
1113+
def client_creator(service_name, **kwargs):
1114+
create_client_kwargs = {'region_name': region_name}
1115+
create_client_kwargs.update(**kwargs)
1116+
return create_nested_client(
1117+
session, service_name, **create_client_kwargs
1118+
)
1119+
1120+
return client_creator

aiobotocore/session.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ async def _create_client(
251251
monitor = self._get_internal_component('monitor')
252252
if monitor is not None:
253253
monitor.register(client.meta.events)
254+
self._register_client_plugins(client)
254255
return client
255256

256257
async def get_available_regions(

aiobotocore/tokens.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import timedelta
44

55
import dateutil.parser
6+
from botocore import UNSIGNED
67
from botocore.compat import total_seconds
78
from botocore.exceptions import ClientError, TokenRetrievalError
89
from botocore.tokens import (
@@ -14,6 +15,9 @@
1415
_utc_now,
1516
)
1617

18+
from aiobotocore.config import AioConfig
19+
from aiobotocore.utils import create_nested_client
20+
1721
logger = logging.getLogger(__name__)
1822

1923

@@ -152,6 +156,14 @@ async def _refresher(self):
152156
token_dict["accessToken"], expiration=expiration
153157
)
154158

159+
@property
160+
def _client(self):
161+
config = AioConfig(
162+
region_name=self._sso_config["sso_region"],
163+
signature_version=UNSIGNED,
164+
)
165+
return create_nested_client(self._session, "sso-oidc", config=config)
166+
155167
def load_token(self, **kwargs):
156168
if self._sso_config is None:
157169
return None

aiobotocore/utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@
2424
IMDSRegionProvider,
2525
InstanceMetadataFetcher,
2626
InstanceMetadataRegionFetcher,
27+
PluginContext,
2728
ReadTimeoutError,
2829
S3ExpressIdentityCache,
2930
S3ExpressIdentityResolver,
3031
S3RegionRedirector,
3132
S3RegionRedirectorv2,
3233
get_environ_proxies,
3334
os,
35+
reset_plugin_context,
3436
resolve_imds_endpoint_mode,
37+
set_plugin_context,
3538
)
3639

3740
import aiobotocore.httpsession
@@ -747,3 +750,35 @@ async def _get_response(self, full_url, headers, timeout):
747750
f"container metadata: {e}"
748751
)
749752
raise MetadataRetrievalError(error_msg=error_msg)
753+
754+
755+
@contextlib.asynccontextmanager
756+
async def create_nested_client(session, service_name, **kwargs):
757+
"""Create a nested client with plugin context disabled.
758+
759+
If a client is created from within a plugin based on the environment variable,
760+
an infinite loop could arise. Any clients created from within another client
761+
must use this method to prevent infinite loops.
762+
763+
This is the async version of botocore.utils.create_nested_client that works
764+
with aiobotocore's async session.
765+
766+
Usage:
767+
async with create_nested_client(session, 'sts', region_name='us-east-1') as client:
768+
response = await client.assume_role(...)
769+
"""
770+
# Set plugin context to disabled
771+
ctx = PluginContext(plugins="DISABLED")
772+
token = set_plugin_context(ctx)
773+
774+
try:
775+
# Create client context
776+
async with session.create_client(service_name, **kwargs) as client:
777+
# Reset plugin context immediately after client creation, matching botocore behavior
778+
reset_plugin_context(token)
779+
token = None
780+
781+
yield client
782+
finally:
783+
if token:
784+
reset_plugin_context(token)

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ classifiers = [
2222
'Programming Language :: Python :: 3.11',
2323
'Programming Language :: Python :: 3.12',
2424
'Programming Language :: Python :: 3.13',
25+
'Programming Language :: Python :: 3.14',
2526
'Environment :: Web Environment',
2627
'Framework :: AsyncIO',
2728
]
@@ -31,7 +32,7 @@ dynamic = ["version", "readme"]
3132
dependencies = [
3233
"aiohttp >= 3.9.2, < 4.0.0",
3334
"aioitertools >= 0.5.1, < 1.0.0",
34-
"botocore >= 1.39.7, < 1.39.9", # NOTE: When updating, always keep `project.optional-dependencies` aligned
35+
"botocore >= 1.39.9, < 1.39.12", # NOTE: When updating, always keep `project.optional-dependencies` aligned
3536
"python-dateutil >= 2.1, < 3.0.0",
3637
"jmespath >= 0.7.1, < 2.0.0",
3738
"multidict >= 6.0.0, < 7.0.0",
@@ -40,10 +41,10 @@ dependencies = [
4041

4142
[project.optional-dependencies]
4243
awscli = [
43-
"awscli >= 1.41.7, < 1.41.9",
44+
"awscli >= 1.41.9, < 1.41.12",
4445
]
4546
boto3 = [
46-
"boto3 >= 1.39.7, < 1.39.9",
47+
"boto3 >= 1.39.9, < 1.39.12",
4748
]
4849
httpx = [
4950
"httpx >= 0.25.1, < 0.29"

tests/test_patches.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
SharedCredentialProvider,
3030
SSOCredentialFetcher,
3131
SSOProvider,
32+
_get_client_creator,
3233
create_credential_resolver,
3334
create_mfa_serial_refresher,
3435
get_credentials,
@@ -109,6 +110,7 @@
109110
S3ExpressIdentityResolver,
110111
S3RegionRedirector,
111112
S3RegionRedirectorv2,
113+
create_nested_client,
112114
)
113115
from botocore.waiter import (
114116
NormalizedOperationMethod,
@@ -651,6 +653,12 @@ def test_protocol_parsers():
651653
'ff0c735a388ac8dd7fe300a32c1e36cdf33c0f56',
652654
},
653655
),
656+
(
657+
_get_client_creator,
658+
{
659+
'6274e5675ecf13180b5a18daa0242bb71d6437b6',
660+
},
661+
),
654662
# configprovider.py
655663
(
656664
SmartDefaultsConfigStoreFactory.merge_smart_defaults,
@@ -1353,6 +1361,12 @@ def test_protocol_parsers():
13531361
'148a10274d3268dd42df05d3bcfb98c668f01086',
13541362
},
13551363
),
1364+
(
1365+
create_nested_client,
1366+
{
1367+
'619a190d84c4871c0e3c4828b0d6ea607d8b89da',
1368+
},
1369+
),
13561370
# waiter.py
13571371
(
13581372
NormalizedOperationMethod.__call__,

0 commit comments

Comments
 (0)