diff --git a/aiobotocore/client.py b/aiobotocore/client.py index fa140326..cb2030a7 100644 --- a/aiobotocore/client.py +++ b/aiobotocore/client.py @@ -1,3 +1,5 @@ +import asyncio + from botocore.auth import resolve_auth_type from botocore.awsrequest import prepare_request_dict from botocore.client import ( @@ -41,26 +43,23 @@ async def create_client( api_version=None, client_config=None, auth_token=None, + load_executor=False, ): responses = await self._event_emitter.emit( 'choose-service-name', service_name=service_name ) service_name = first_non_none_response(responses, default=service_name) - service_model = self._load_service_model(service_name, api_version) - try: - endpoints_ruleset_data = self._load_service_endpoints_ruleset( - service_name, api_version - ) - partition_data = self._loader.load_data('partitions') - except UnknownServiceError: - endpoints_ruleset_data = None - partition_data = None - logger.info( - 'No endpoints ruleset found for service %s, falling back to ' - 'legacy endpoint routing.', - service_name, + logger.debug( + "AioClientCreator - Method load_service_model[botocore] could generate I/O. Running in executor: %s", + load_executor, + ) + if load_executor: + model_data = await asyncio.get_running_loop().run_in_executor( + None, self._load_models, service_name, api_version ) - + else: + model_data = self._load_models(service_name, api_version) + service_model, endpoints_ruleset_data, partition_data = model_data cls = await self._create_client_class(service_name, service_model) region_name, client_config = self._normalize_fips_region( region_name, client_config @@ -109,6 +108,23 @@ async def create_client( ) return service_client + def _load_models(self, service_name, api_version): + service_model = self._load_service_model(service_name, api_version) + try: + endpoints_ruleset_data = self._load_service_endpoints_ruleset( + service_name, api_version + ) + partition_data = self._loader.load_data('partitions') + except UnknownServiceError: + endpoints_ruleset_data = None + partition_data = None + logger.info( + 'No endpoints ruleset found for service %s, falling back to ' + 'legacy endpoint routing.', + service_name, + ) + return service_model, endpoints_ruleset_data, partition_data + async def _create_client_class(self, service_name, service_model): class_attributes = self._create_methods(service_model) py_name_to_operation_name = self._create_name_mapping(service_model) diff --git a/aiobotocore/session.py b/aiobotocore/session.py index be40b7e0..d852171c 100644 --- a/aiobotocore/session.py +++ b/aiobotocore/session.py @@ -1,7 +1,12 @@ +import asyncio +import functools + from botocore import UNSIGNED from botocore import __version__ as botocore_version from botocore import translate +from botocore.client import logger from botocore.exceptions import PartialCredentialsError +from botocore.loaders import Loader from botocore.session import EVENT_ALIASES, ServiceModel from botocore.session import Session as _SyncSession from botocore.session import UnknownServiceError, copy @@ -37,6 +42,7 @@ def __init__( event_hooks=None, include_builtin_handlers=True, profile=None, + load_executor=False, ): if event_hooks is None: event_hooks = AioHierarchicalEmitter() @@ -46,6 +52,7 @@ def __init__( ) self._set_user_agent_for_session() + self.load_executor = load_executor def _set_user_agent_for_session(self): # Mimic approach taken by AWS's aws-cli project @@ -103,9 +110,25 @@ async def get_service_data(self, service_name, api_version=None): Retrieve the fully merged data associated with a service. """ data_path = service_name - service_data = self.get_component('data_loader').load_service_model( - data_path, type_name='service-2', api_version=api_version + data_loader: Loader = self.get_component('data_loader') + logger.debug( + "AioSession - Method load_service_model[botocore] could generate I/O. Running in executor: %s", + self.load_executor, ) + if self.load_executor: + service_data = await asyncio.get_event_loop().run_in_executor( + None, + functools.partial( + data_loader.load_service_model, + data_path, + type_name='service-2', + api_version=api_version, + ), + ) + else: + service_data = data_loader.load_service_model( + data_path, type_name='service-2', api_version=api_version + ) service_id = EVENT_ALIASES.get(service_name, service_name) await self._events.emit( f'service-data-loaded.{service_id}', @@ -223,6 +246,7 @@ async def _create_client( client_config=config, api_version=api_version, auth_token=auth_token, + load_executor=self.load_executor, ) monitor = self._get_internal_component('monitor') if monitor is not None: