Universal Command Line Environment for AWS.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Event-driven plugin architecture for extending AWS CLI functionality with custom handlers, commands, and integrations. The plugin system enables seamless extension of AWS CLI capabilities without modifying core code.
Core function for loading and initializing plugins with the AWS CLI.
def load_plugins(plugin_mapping, event_hooks=None, include_builtins=True):
"""
Load and initialize plugins for AWS CLI.
Parameters:
plugin_mapping: dict, mapping of plugin names to module paths
event_hooks: botocore event emitter, optional event system
include_builtins: bool, whether to load built-in plugins (default True)
"""Pre-defined plugins that are loaded by default.
BUILTIN_PLUGINS: dict # {'__builtin__': 'awscli.handlers'}Usage Example:
from awscli.plugin import load_plugins
# Load custom plugins
custom_plugins = {
'my-plugin': 'mypackage.aws_plugin',
'dev-tools': 'devtools.aws_extensions'
}
load_plugins(custom_plugins, include_builtins=True)# mypackage/aws_plugin.py
def awscli_initialize(cli):
"""
Plugin initialization function called by AWS CLI.
Parameters:
cli: AWS CLI instance for registration
"""
# Register event handlers
cli.register('before-call', modify_request)
cli.register('after-call', process_response)
# Register custom commands
cli.register('building-command-table.main', add_commands)
def modify_request(event_name=None, **kwargs):
"""Handle before-call events."""
print(f"Intercepting request: {event_name}")
def process_response(parsed, **kwargs):
"""Handle after-call events."""
print(f"Processing response: {parsed}")
def add_commands(command_table, session, **kwargs):
"""Add custom commands to command table."""
from mypackage.commands import CustomCommand
command_table['custom'] = CustomCommand(session)def register_handlers(cli):
"""Register various event handlers."""
# Command table modification
cli.register('building-command-table.main', add_main_commands)
cli.register('building-command-table.s3', add_s3_commands)
# Argument table modification
cli.register('building-argument-table.s3.ls', add_s3_ls_args)
# Request/response processing
cli.register('before-call', modify_all_requests)
cli.register('before-call.s3.ListObjects', modify_s3_list_request)
cli.register('after-call', process_all_responses)
# Output processing
cli.register('doc-output', modify_documentation)def add_s3_commands(command_table, session, **kwargs):
"""Add custom S3 commands."""
from mypackage.s3_commands import S3SyncAdvanced
command_table['sync-advanced'] = S3SyncAdvanced(session)
def add_s3_ls_args(argument_table, **kwargs):
"""Add arguments to s3 ls command."""
from awscli.arguments import CustomArgument
argument_table['include-metadata'] = CustomArgument(
'include-metadata',
help_text='Include object metadata in output',
action='store_true'
)def modify_requests(event_name, endpoint, request_dict, **kwargs):
"""Modify AWS service requests."""
# Add custom headers
if 'headers' not in request_dict:
request_dict['headers'] = {}
request_dict['headers']['X-Custom-Plugin'] = 'MyPlugin-1.0'
# Modify parameters
if 'params' in request_dict:
# Add default parameters
request_dict['params'].setdefault('MaxItems', 100)def process_responses(parsed, **kwargs):
"""Process AWS service responses."""
# Add custom fields
if isinstance(parsed, dict):
parsed['_plugin_processed'] = True
parsed['_processing_time'] = time.time()
# Transform data
if 'Instances' in parsed:
for instance in parsed['Instances']:
# Add computed fields
instance['_display_name'] = f"{instance.get('InstanceId', 'unknown')}"Plugins can be configured through AWS CLI configuration:
# ~/.aws/config
[plugins]
my-plugin = mypackage.aws_plugin
dev-tools = devtools.aws_extensionsimport os
from awscli.plugin import load_plugins
# Load plugins from environment
plugin_paths = os.environ.get('AWS_CLI_PLUGINS', '').split(',')
plugin_mapping = {}
for plugin_path in plugin_paths:
if plugin_path.strip():
name = plugin_path.split('.')[-1]
plugin_mapping[name] = plugin_path.strip()
load_plugins(plugin_mapping)def conditional_load(cli):
"""Load plugins based on conditions."""
# Only load in development
if os.environ.get('AWS_CLI_ENV') == 'development':
cli.register('building-command-table.main', add_dev_commands)
# Load based on AWS profile
current_profile = cli.session.get_config_variable('profile')
if current_profile == 'production':
cli.register('before-call', add_production_safeguards)mypackage/
├── __init__.py
├── aws_plugin.py # Main plugin module
├── commands/
│ ├── __init__.py
│ └── custom_commands.py # Custom command implementations
├── handlers/
│ ├── __init__.py
│ └── event_handlers.py # Event handler implementations
└── setup.py # Package configuration# setup.py
from setuptools import setup, find_packages
setup(
name='aws-cli-my-plugin',
version='1.0.0',
packages=find_packages(),
install_requires=[
'awscli>=1.40.0',
'botocore>=1.30.0'
],
entry_points={
'console_scripts': [
'aws-my-plugin=mypackage.cli:main'
]
}
)import unittest
from awscli.testutils import BaseCLIDriverTest
class TestMyPlugin(BaseCLIDriverTest):
def setUp(self):
super().setUp()
# Load plugin for testing
from mypackage.aws_plugin import awscli_initialize
awscli_initialize(self.driver)
def test_custom_command(self):
"""Test custom command functionality."""
result = self.run_cmd(['custom', '--help'])
self.assertEqual(result.rc, 0)
self.assertIn('Custom command', result.stdout)
def test_event_handler(self):
"""Test event handler integration."""
result = self.run_cmd(['s3', 'ls'])
# Verify plugin modifications
self.assertIn('plugin_processed', result.stdout)Install with Tessl CLI
npx tessl i tessl/pypi-awscli