Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a30575a
Implement conditional autoprompt importing.
aemous Apr 15, 2026
c2a322c
Minor formatting change.
aemous Apr 20, 2026
8762f7a
Fix tests based on new autoprompt interface.
aemous Apr 20, 2026
389cd83
Fix remaining eager imports of prompttoolkit and autoprompt.
aemous Apr 20, 2026
e82544d
Add new autoprompt.exceptions file.
aemous Apr 20, 2026
4a96adf
Refactor Configure SSO and SSOSession commands to achieve lazy import…
aemous Apr 20, 2026
9491794
Remove year from license in sso_commands.py file.
aemous Apr 20, 2026
50f107e
Add module-level comment explaining new sso_commands.py module.
aemous Apr 20, 2026
05b657e
Fix bug where provided runners on wizardcommands were not being propo…
aemous Apr 21, 2026
bf888ff
Add changelog entry.
aemous Apr 21, 2026
2e7ae88
Update enhancement-Performance-97002.json
aemous Apr 24, 2026
4a3cf3b
Defer import of prompt_toolkit for 'aws configure' commands until the…
aemous Apr 24, 2026
de3aad1
Refactor logs customization to defer prompt_toolkit imports.
aemous Apr 27, 2026
adee5c9
Rename test file and update imports after logs refactor.
aemous Apr 27, 2026
509f9b3
Changes based on feedback.
aemous Apr 28, 2026
cc38ce1
First pass of pre-commit.
aemous Apr 28, 2026
f82c3f8
Second pass of pre-commit.
aemous Apr 28, 2026
4400280
Third pass of pre-commit
aemous Apr 28, 2026
f24425b
Fix new tests on Windows by porting existing BaseAWSCommandParamsTest…
aemous Apr 28, 2026
7542c33
Raise timeout to 60s in new tests to address flakiness on Windows run…
aemous Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-Performance-97002.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "Performance",
"description": "Defer some imports (e.g. ``prompt_toolkit``) until they are needed to reduce command initialization time (e.g. loading all imported modules)."
}
35 changes: 0 additions & 35 deletions awscli/autoprompt/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,13 @@
# 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.
from botocore.exceptions import ProfileNotFound

from awscli.autocomplete.filters import fuzzy_filter
from awscli.autocomplete.main import create_autocompleter
from awscli.autoprompt.prompttoolkit import PromptToolkitPrompter
from awscli.customizations.exceptions import ParamValidationError
from awscli.errorhandler import SilenceParamValidationMsgErrorHandler


class AutoPromptDriver:
_NO_PROMPT_ARGS = ['help', '--version']
_CLI_AUTO_PROMPT_OPTION = '--cli-auto-prompt'
_NO_CLI_AUTO_PROMPT_OPTION = '--no-cli-auto-prompt'

def __init__(self, driver, completion_source=None, prompter=None):
self._completion_source = completion_source
self._prompter = prompter
Expand All @@ -42,34 +35,6 @@ def prompter(self):
)
return self._prompter

def validate_auto_prompt_args_are_mutually_exclusive(self, args):
no_cli_auto_prompt = self._NO_CLI_AUTO_PROMPT_OPTION in args
cli_auto_prompt = self._CLI_AUTO_PROMPT_OPTION in args
if cli_auto_prompt and no_cli_auto_prompt:
raise ParamValidationError(
'Both --cli-auto-prompt and --no-cli-auto-prompt cannot be '
'specified at the same time.'
)

def resolve_mode(self, args):
# Order of precedence to check:
# - check if any arg rom NO_PROMPT_ARGS in args
# - check if '--no-cli-auto-prompt' was specified
# - check if '--cli-auto-prompt' was specified
# - check configuration chain
self.validate_auto_prompt_args_are_mutually_exclusive(args)
if any(arg in args for arg in self._NO_PROMPT_ARGS):
return 'off'
if self._NO_CLI_AUTO_PROMPT_OPTION in args:
return 'off'
if self._CLI_AUTO_PROMPT_OPTION in args:
return 'on'
try:
config = self._session.get_config_variable('cli_auto_prompt')
return config.lower()
except ProfileNotFound:
return 'off'

def inject_silence_param_error_msg_handler(self, driver):
driver.error_handler.inject_handler(
0, SilenceParamValidationMsgErrorHandler()
Expand Down
14 changes: 14 additions & 0 deletions awscli/autoprompt/exceptions.py
Comment thread
aemous marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# 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.
class PrompterKeyboardInterrupt(KeyboardInterrupt):
pass
5 changes: 1 addition & 4 deletions awscli/autoprompt/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from prompt_toolkit.layout.processors import BeforeInput
from prompt_toolkit.widgets import SearchToolbar, VerticalLine

from awscli.autoprompt.exceptions import PrompterKeyboardInterrupt
from awscli.autoprompt.filters import (
doc_section_visible,
doc_window_has_focus,
Expand All @@ -46,10 +47,6 @@
)


class PrompterKeyboardInterrupt(KeyboardInterrupt):
pass


class CLIPromptBuffer(Buffer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down
45 changes: 42 additions & 3 deletions awscli/clidriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
ScopedConfigProvider,
)
from botocore.context import start_as_current_context
from botocore.exceptions import ProfileNotFound
from botocore.history import get_global_history_recorder

from awscli import __version__
Expand All @@ -49,7 +50,6 @@
ListArgument,
UnknownArgumentError,
)
from awscli.autoprompt.core import AutoPromptDriver
from awscli.commands import CLICommand
from awscli.compat import (
default_pager,
Expand All @@ -58,6 +58,7 @@
validate_preferred_output_encoding,
)
from awscli.constants import PARAM_VALIDATION_ERROR_RC
from awscli.customizations.exceptions import ParamValidationError
from awscli.errorhandler import (
construct_cli_error_handlers_chain,
construct_entry_point_handlers_chain,
Expand Down Expand Up @@ -90,6 +91,9 @@
)
HISTORY_RECORDER = get_global_history_recorder()
METADATA_FILENAME = 'metadata.json'
_NO_AUTO_PROMPT_ARGS = ['help', '--version']
_CLI_AUTO_PROMPT_OPTION = '--cli-auto-prompt'
_NO_CLI_AUTO_PROMPT_OPTION = '--no-cli-auto-prompt'
# Don't remove this line. The idna encoding
# is used by getaddrinfo when dealing with unicode hostnames,
# and in some cases, there appears to be a race condition
Expand Down Expand Up @@ -126,6 +130,36 @@ def create_clidriver(args=None):
return driver


def validate_auto_prompt_args_are_mutually_exclusive(args):
no_cli_auto_prompt = _NO_CLI_AUTO_PROMPT_OPTION in args
cli_auto_prompt = _CLI_AUTO_PROMPT_OPTION in args
if cli_auto_prompt and no_cli_auto_prompt:
raise ParamValidationError(
'Both --cli-auto-prompt and --no-cli-auto-prompt cannot be '
'specified at the same time.'
)


def resolve_auto_prompt_mode(args, session):
# Order of precedence to check:
# - check if any arg from _NO_AUTO_PROMPT_ARGS in args
# - check if '--no-cli-auto-prompt' was specified
# - check if '--cli-auto-prompt' was specified
# - check configuration chain
validate_auto_prompt_args_are_mutually_exclusive(args)
if any(arg in args for arg in _NO_AUTO_PROMPT_ARGS):
return 'off'
if _NO_CLI_AUTO_PROMPT_OPTION in args:
return 'off'
if _CLI_AUTO_PROMPT_OPTION in args:
return 'on'
try:
config = session.get_config_variable('cli_auto_prompt')
return config.lower()
except ProfileNotFound:
return 'off'


def _get_distribution_source():
metadata_file = os.path.join(
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data'),
Expand Down Expand Up @@ -220,12 +254,17 @@ def _do_main(self, args):
driver = self._driver
if driver is None:
driver = create_clidriver(args)
autoprompt_driver = AutoPromptDriver(driver)
auto_prompt_mode = autoprompt_driver.resolve_mode(args)
auto_prompt_mode = resolve_auto_prompt_mode(args, driver.session)
if auto_prompt_mode == 'on':
from awscli.autoprompt.core import AutoPromptDriver

autoprompt_driver = AutoPromptDriver(driver)
args = autoprompt_driver.prompt_for_args(args)
rc = self._run_driver(driver, args, prompt_mode='on')
elif auto_prompt_mode == 'on-partial':
from awscli.autoprompt.core import AutoPromptDriver

autoprompt_driver = AutoPromptDriver(driver)
autoprompt_driver.inject_silence_param_error_msg_handler(driver)
rc = self._run_driver(driver, args, prompt_mode='off')
if rc == PARAM_VALIDATION_ERROR_RC:
Expand Down
2 changes: 1 addition & 1 deletion awscli/customizations/configure/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from awscli.customizations.configure.listprofiles import ListProfilesCommand
from awscli.customizations.configure.mfalogin import ConfigureMFALoginCommand
from awscli.customizations.configure.set import ConfigureSetCommand
from awscli.customizations.configure.sso import (
from awscli.customizations.configure.sso_commands import (
ConfigureSSOCommand,
ConfigureSSOSessionCommand,
)
Expand Down
Loading
Loading