steering_docs/python-tech/basics_scenario.md
šØ CRITICAL - Must be completed BEFORE any code generation
# Step 1: List available knowledge bases
ListKnowledgeBases()
# Step 2: Query coding standards (REQUIRED)
QueryKnowledgeBases("coding-standards-KB", "Python-code-example-standards")
# Step 3: Query implementation patterns (REQUIRED)
QueryKnowledgeBases("Python-premium-KB", "Python implementation patterns structure")
# Step 4: AWS service research (REQUIRED)
search_documentation("What is [AWS Service] and what are its key API operations?")
read_documentation("https://docs.aws.amazon.com/[service]/latest/[relevant-page]")
FAILURE TO COMPLETE KNOWLEDGE BASE CONSULTATION WILL RESULT IN INCORRECT CODE STRUCTURE
Generate interactive scenarios that demonstrate complete workflows using multiple service operations in a guided, educational manner. Implementation must be based on the service SPECIFICATION.md file.
scenarios/basics/{service}/SPECIFICATION.mdpython/example_code/{service}/
āāā scenario_{service}_basics.py # Main scenario file
CRITICAL: Always read scenarios/basics/{service}/SPECIFICATION.md first to understand:
From the specification, identify:
CRITICAL: Wrapper classes MUST use the correct snippet tag structure to ensure proper metadata integration and test compatibility.
For Wrapper Class Declaration:
# snippet-start:[python.example_code.{service}.{Service}Wrapper.decl]
class {Service}Wrapper:
"""Wrapper class for managing {Service} operations."""
# Class implementation...
# snippet-end:[python.example_code.{service}.{Service}Wrapper.decl]
For Individual Methods:
# snippet-start:[python.example_code.{service}.MethodName]
def method_name(self, parameter: str) -> bool:
"""Method implementation."""
# Method code...
# snippet-end:[python.example_code.{service}.MethodName]
MANDATORY: When wrapper classes are used in scenarios, the metadata files MUST reference the .decl tag pattern:
Correct Metadata Reference:
python/cross_service/example_scenario:
excerpts:
- snippet_tags:
- python.example_code.{service}.{ServiceName}Wrapper.decl # ā CORRECT
- python.example_code.{service}.MethodName
Incorrect Metadata Reference:
python/cross_service/example_scenario:
excerpts:
- snippet_tags:
- python.example_code.{service}.{ServiceName}Wrapper # ā INCORRECT
- python.example_code.{service}.MethodName
.decl pattern ensures stubbed tests can properly mock wrapper class declarationsIn Wrapper File ({service}_wrapper.py):
# snippet-start:[python.example_code.{service_name}.{Service}Wrapper.decl]
class {Service}Wrapper:
"""Wrapper class for managing {AWS Service} operations."""
def __init__(self, {service}_client: Any) -> None:
"""Initialize the {Service}Wrapper."""
self.{service}_client = {service}_client
# snippet-end:[python.example_code.{service_name}.{Service}Wrapper.decl]
# snippet-start:[python.example_code.{service}.CreateResource]
def create_resource(self, name: str) -> str:
"""Create a resource."""
# Method implementation...
# snippet-end:[python.example_code.{service}.CreateResource]
In Metadata File (.doc_gen/metadata/{service}_metadata.yaml):
For Individual Service Operations:
{service}_CreateResource:
languages:
Python:
versions:
- sdk_version: 3
github: python/example_code/{service}
excerpts:
- snippet_tags:
- python.example_code.{serviceName}.{ServiceName}Wrapper.decl
- python.example_code.{serviceName}.CreateResource
For Scenario Sections (CRITICAL - Must Include Both Scenario and Wrapper Classes):
{service}_Scenario_Basics:
languages:
Python:
versions:
- sdk_version: 3
github: python/example_code/{service}
excerpts:
- description: Run an interactive scenario at a command prompt.
snippet_tags:
- python.example_code.{service}.{ServiceName}Scenario
- description: Create a class that wraps service operations.
snippet_tags:
- python.example_code.{service}.{ServiceName}Wrapper.decl
Why Both Are Required in Scenario Sections:
Example Based on Topics and Queues:
sqs_Scenario_TopicsAndQueues:
languages:
Python:
versions:
- sdk_version: 3
github: python/cross_service/scenario_name
excerpts:
- description: Run an interactive scenario at a command prompt.
snippet_tags:
- python.example_code.cross_service.scenario_name.FeatureScenario
- description: Create classes that wrap service operations.
snippet_tags:
- python.example_code.service.ServiceWrapper.decl
- python.example_code.service.ServiceWrapper.Action
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Purpose
Shows how to use {AWS Service} to {scenario description}. This scenario demonstrates:
1. {Phase 1 description}
2. {Phase 2 description}
3. {Phase 3 description}
4. {Phase 4 description}
This example uses the AWS SDK for Python (Boto3) to interact with {AWS Service}.
"""
import logging
import sys
import time
from typing import List, Dict, Any
import boto3
from botocore.exceptions import ClientError
from {service}_wrapper import {Service}Wrapper
# Add relative path to include demo_tools
sys.path.insert(0, "../..")
from demo_tools import question as q
logger = logging.getLogger(__name__)
# snippet-start:[python.example_code.{service}.{Service}Scenario]
class {Service}Scenario:
"""Runs an interactive scenario that shows how to use {AWS Service}."""
def __init__(self, {service}_wrapper: {Service}Wrapper):
"""
:param {service}_wrapper: An instance of the {Service}Wrapper class.
"""
self.{service}_wrapper = {service}_wrapper
self.{resource_id} = None
def run_scenario(self):
"""Runs the {AWS Service} basics scenario."""
print("-" * 88)
print("Welcome to the {AWS Service} basics scenario!")
print("-" * 88)
print(
"{Service description and what users will learn}"
)
print()
try:
self._setup_phase()
self._demonstration_phase()
self._examination_phase()
except Exception as e:
logger.error(f"Scenario failed: {e}")
print(f"The scenario encountered an error: {e}")
finally:
self._cleanup_phase()
def _setup_phase(self):
"""Setup phase: Implement based on specification's Setup section."""
print("Setting up {AWS Service}...")
print()
# Example: Check for existing resources (from specification)
existing_resources = self.{service}_wrapper.list_resources()
if existing_resources:
print(f"Found {len(existing_resources)} existing resource(s):")
for resource in existing_resources:
print(f" - {resource}")
use_existing = q.ask(
"Would you like to use an existing resource? (y/n): ", q.is_yesno
)
if use_existing:
self.resource_id = existing_resources[0]
return
def _demonstration_phase(self):
"""Demonstration phase: Implement operations from specification."""
print("Demonstrating {AWS Service} capabilities...")
print()
# Implement specific operations from specification
# Example: Generate sample data if specified
self.{service}_wrapper.create_sample_data(self.resource_id)
print("ā Sample data created successfully")
# Wait if specified in the specification
print("Waiting for data to be processed...")
time.sleep(5)
def _examination_phase(self):
"""Examination phase: Implement data analysis from specification."""
print("Examining {AWS Service} data...")
print()
# List and examine data as specified
data_items = self.{service}_wrapper.list_data(self.resource_id)
if not data_items:
print("No data found. Data may take a few minutes to appear.")
return
print(f"Found {len(data_items)} data item(s)")
# Get detailed information as specified
detailed_data = self.{service}_wrapper.get_data_details(self.resource_id, data_items[:5])
self._display_data_summary(detailed_data)
# Show detailed view if specified
if detailed_data:
show_details = q.ask(
"Would you like to see detailed information? (y/n): ", q.is_yesno
)
if show_details:
self._display_data_details(detailed_data[0])
# Filter data as specified
self._filter_data_by_criteria(data_items)
def _cleanup_phase(self):
"""Cleanup phase: Implement cleanup options from specification."""
if not self.resource_id:
return
print("Cleanup options:")
print("Note: Deleting the resource will stop all monitoring/processing.")
delete_resource = q.ask(
"Would you like to delete the resource? (y/n): ", q.is_yesno
)
if delete_resource:
try:
self.{service}_wrapper.delete_resource(self.resource_id)
print(f"ā Deleted resource: {self.resource_id}")
except Exception as e:
print(f"Error deleting resource: {e}")
else:
print(f"Resource {self.resource_id} will continue running.")
print("You can manage it through the AWS Console or delete it later.")
# snippet-end:[python.example_code.{service}.{Service}Scenario]
def main():
"""Runs the {AWS Service} basics scenario."""
logging.basicConfig(level=logging.WARNING, format="%(levelname)s: %(message)s")
try:
{service}_wrapper = {Service}Wrapper.from_client()
scenario = {Service}Scenario({service}_wrapper)
scenario.run_scenario()
except Exception as e:
logger.error(f"Failed to run scenario: {e}")
print(f"Failed to run the scenario: {e}")
if __name__ == "__main__":
main()
# Yes/No questions
use_existing = q.ask("Use existing resource? (y/n): ", q.is_yesno)
# Text input
resource_name = q.ask("Enter resource name: ")
# Numeric input
count = q.ask("How many items? ", q.is_int)
# Progress indicators
print("ā Operation completed successfully")
print("ā Warning message")
print("ā Error occurred")
# Formatted output
print("-" * 60)
print(f"Found {len(items)} items:")
for item in items:
print(f" ⢠{item['name']}")
The specification includes an "Errors" section with specific error codes and handling:
# Example error handling based on specification
try:
response = self.{service}_wrapper.create_resource()
return response
except ClientError as e:
error_code = e.response["Error"]["Code"]
if error_code == "BadRequestException":
# Handle as specified: "Validate input parameters and notify user"
print("Invalid configuration. Please check your parameters.")
elif error_code == "InternalServerErrorException":
# Handle as specified: "Retry operation with exponential backoff"
print("Service temporarily unavailable. Retrying...")
# Implement retry logic
else:
print(f"Unexpected error: {e}")
raise
boto3>=1.26.137
botocore>=1.29.137
pytest>=7.0.0
pytest-mock>=3.10.0
moto>=4.0.0
MANDATORY: All feature scenarios MUST include both stubbed unit tests and live integration tests:
test_tools for fast, reliable unit testing@pytest.mark.integ decorator to test against real AWS servicesCRITICAL: Before implementing tests, verify that all wrapper methods have corresponding stubber methods available in python/test_tools/{service}_stubber.py.
Common service stubbers available in python/test_tools/:
sns_stubber.py - Amazon SNS operationssqs_stubber.py - Amazon SQS operationss3_stubber.py - Amazon S3 operationsdynamodb_stubber.py - Amazon DynamoDB operationslambda_stubber.py - AWS Lambda operationsiam_stubber.py - AWS IAM operationscloudformation_stubber.py - AWS CloudFormation operations{service}_stubber.py exists in python/test_tools/SNS Wrapper ā SNS Stubber Mapping:
# SNS Wrapper Methods ā Available Stubber Methods
create_topic() ā stub_create_topic()
subscribe_queue_to_topic() ā stub_subscribe()
publish_message() ā stub_publish()
unsubscribe() ā stub_unsubscribe()
delete_topic() ā stub_delete_topic()
list_topics() ā stub_list_topics()
SQS Wrapper ā SQS Stubber Mapping:
# SQS Wrapper Methods ā Available Stubber Methods
create_queue() ā stub_create_queue()
get_queue_arn() ā stub_get_queue_attributes()
set_queue_policy_for_topic() ā stub_set_queue_attributes()
receive_messages() ā stub_receive_messages()
delete_messages() ā stub_delete_message_batch()
delete_queue() ā stub_delete_queue()
list_queues() ā stub_list_queues()
send_message() ā stub_send_message()
If a wrapper method doesn't have a corresponding stubber method:
_stub_bifurcator directlyCRITICAL: Always verify that stubber method parameters match the actual wrapper implementation calls. Mismatched parameters will cause "Unexpected API Call" errors.
Problem: Error getting response stub for operation CreateTopic: Unexpected API Call: A call was made but no additional calls expected.
Root Cause: The wrapper method passes different parameters than what the stubber expects.
Wrapper Implementation Analysis:
# From sns_wrapper.py create_topic method:
attributes = {}
if is_fifo:
attributes['FifoTopic'] = 'true'
if content_based_deduplication:
attributes['ContentBasedDeduplication'] = 'true'
response = self.sns_client.create_topic(
Name=topic_name,
Attributes=attributes # ALWAYS passed, even if empty for standard topics
)
Stubber Method Signature:
# From sns_stubber.py:
def stub_create_topic(self, topic_name, topic_arn, topic_attributes=None, error_code=None):
expected_params = {"Name": topic_name}
if topic_attributes is not None:
expected_params["Attributes"] = topic_attributes
Correct Test Stub Usage:
# For standard (non-FIFO) topic - MUST include empty attributes
runner.add(
mock_mgr.sns_stubber.stub_create_topic,
"test-topic", # topic_name
"arn:aws:sns:us-east-1:123456789012:test-topic", # topic_arn
{}, # topic_attributes (empty dict for standard)
)
# For FIFO topic - include FIFO attributes
runner.add(
mock_mgr.sns_stubber.stub_create_topic,
"test-topic.fifo", # topic_name
"arn:aws:sns:us-east-1:123456789012:test-topic.fifo", # topic_arn
{"FifoTopic": "true"}, # topic_attributes (FIFO settings)
)
Step 1: Enable detailed logging to see actual vs expected parameters:
import logging
logging.basicConfig(level=logging.DEBUG)
Step 2: Compare wrapper client call with stubber expected_params:
expected_params dictionary constructionStep 3: Common parameter patterns:
# SNS Operations
create_topic() ā Always passes Attributes (even if empty dict)
subscribe() ā May include Attributes for filter policies
publish() ā May include MessageAttributes, MessageGroupId, etc.
# SQS Operations
create_queue() ā Always passes Attributes (even if empty dict)
receive_message() ā Always passes MessageAttributeNames, WaitTimeSeconds
send_message() ā May include MessageAttributes, DelaySeconds, etc.
Integration tests should use the established stubbing pattern from test_tools and verify no errors are logged:
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Integration tests for {Service} scenario.
"""
import pytest
from unittest.mock import patch
import logging
import boto3
import sys
import os
# Add parent directory to path to import scenario modules
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Add test_tools to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "test_tools"))
from {scenario_name}_scenario import {Service}Scenario
from {service}_wrapper import {Service}Wrapper
class MockManager:
"""Mock manager for the {Service} scenario tests."""
def __init__(self, {service}_client, {service}_stubber, stub_runner):
"""{Service} test setup manager."""
self.{service}_client = {service}_client
self.{service}_stubber = {service}_stubber
self.stub_runner = stub_runner
self.{service}_wrapper = {Service}Wrapper({service}_client)
self.scenario = {Service}Scenario(self.{service}_wrapper)
@pytest.fixture
def mock_mgr(make_stubber, stub_runner):
"""Create a mock manager with AWS service stubbers."""
{service}_client = boto3.client('{service}')
{service}_stubber = make_stubber({service}_client)
return MockManager({service}_client, {service}_stubber, stub_runner)
class TestScenarioIntegration:
"""Integration tests for the {Service} scenario."""
@patch('demo_tools.question.ask')
def test_scenario_integration_no_errors_logged(self, mock_ask, mock_mgr, caplog):
"""
Verify the scenario runs without logging any errors.
Args:
mock_ask: Mock for user input
mock_mgr: Mock manager with stubbed AWS clients
caplog: Pytest log capture fixture
"""
# Arrange user inputs
mock_ask.side_effect = [
"test-resource-name", # Resource name input
"test-parameter", # Parameter input
True, # Cleanup confirmation
]
# Set up stubs for AWS operations
with mock_mgr.stub_runner(None, None) as runner:
# Add stubs for each AWS operation the scenario will perform
# Example for SNS operations:
runner.add(
mock_mgr.sns_stubber.stub_create_topic,
"test-topic",
"arn:aws:sns:us-east-1:123456789012:test-topic",
)
runner.add(
mock_mgr.sns_stubber.stub_subscribe,
"arn:aws:sns:us-east-1:123456789012:test-topic",
"sqs",
"arn:aws:sqs:us-east-1:123456789012:test-queue",
"arn:aws:sns:us-east-1:123456789012:test-topic:subscription-id",
)
runner.add(
mock_mgr.sns_stubber.stub_publish,
"Test message",
"message-id-123",
topic_arn="arn:aws:sns:us-east-1:123456789012:test-topic",
)
runner.add(
mock_mgr.sns_stubber.stub_delete_topic,
"arn:aws:sns:us-east-1:123456789012:test-topic",
)
# Example for SQS operations:
runner.add(
mock_mgr.sqs_stubber.stub_create_queue,
"test-queue",
{},
"https://sqs.us-east-1.amazonaws.com/123456789012/test-queue",
)
runner.add(
mock_mgr.sqs_stubber.stub_get_queue_attributes,
"https://sqs.us-east-1.amazonaws.com/123456789012/test-queue",
"arn:aws:sqs:us-east-1:123456789012:test-queue",
)
runner.add(
mock_mgr.sqs_stubber.stub_receive_messages,
"https://sqs.us-east-1.amazonaws.com/123456789012/test-queue",
[{"body": "Test message"}],
1,
)
runner.add(
mock_mgr.sqs_stubber.stub_delete_queue,
"https://sqs.us-east-1.amazonaws.com/123456789012/test-queue",
)
# Act - Run the scenario
with caplog.at_level(logging.ERROR):
mock_mgr.scenario.run_scenario()
# Assert no errors logged
error_logs = [record for record in caplog.records if record.levelno >= logging.ERROR]
assert len(error_logs) == 0, f"Expected no error logs, but found: {error_logs}"
@patch('demo_tools.question.ask')
def test_scenario_handles_aws_errors_gracefully(self, mock_ask, mock_mgr):
"""Test that the scenario handles AWS errors gracefully."""
# Arrange
mock_ask.side_effect = [
"test-resource-name", # Resource name
]
# Set up stub to simulate AWS error
with mock_mgr.stub_runner("TestException", 0) as runner:
runner.add(
mock_mgr.{service}_stubber.stub_create_resource,
"test-resource-name",
"arn:aws:{service}:us-east-1:123456789012:resource/test-resource",
)
# Act & Assert - Should handle error gracefully
mock_mgr.scenario.run_scenario()
# Verify error was logged appropriately (scenario should continue or cleanup)
### Live Integration Test Pattern
**MANDATORY**: Always include a live integration test that uses real AWS services:
```python
@pytest.mark.integ
def test_run_scenario_integ(input_mocker, capsys):
"""Test the scenario with an integration test using live AWS services."""
# Mock user inputs for automated testing
answers = [
"test-resource-name-integ", # Resource name
"test-parameter", # Parameter input
True, # Cleanup confirmation
]
input_mocker.mock_answers(answers)
# Create real AWS clients (no stubs)
{service}_client = boto3.client('{service}')
# Initialize wrappers and scenario with real clients
{service}_wrapper = {Service}Wrapper({service}_client)
scenario = {Service}Scenario({service}_wrapper)
# Run the scenario with live AWS services
scenario.run_scenario()
# Verify the scenario completed successfully
captured = capsys.readouterr()
assert "scenario completed successfully" in captured.out
Key Features of Live Integration Tests:
@pytest.mark.integ decorator - Allows selective test executioninput_mocker to simulate user interactionsRunning Integration Tests:
# Run only unit tests (stubbed)
pytest test/test_scenario.py
# Run only integration tests (live AWS)
pytest test/test_scenario.py -m integ
# Run all tests
pytest test/test_scenario.py -m "not integ" && pytest test/test_scenario.py -m integ
scenarios/basics/{service}/SPECIFICATION.mdscenarios/basics/{service}/SPECIFICATION.md thoroughly