Skip to content

[Python] Add ecs Basics scenario + Hello - #8048

Open
RuiqiGao-aws wants to merge 5 commits into
awsdocs:mainfrom
RuiqiGao-aws:codeloom/ecs-basics-20260727
Open

[Python] Add ecs Basics scenario + Hello#8048
RuiqiGao-aws wants to merge 5 commits into
awsdocs:mainfrom
RuiqiGao-aws:codeloom/ecs-basics-20260727

Conversation

@RuiqiGao-aws

Copy link
Copy Markdown
Collaborator

Auto-generated Python code examples for ecs (Hello + Basics scenario + wrapper + tests)

@github-actions github-actions Bot added the Python This issue relates to the AWS SDK for Python (boto3) label Jul 27, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Example Review

This PR is a conditional pass with several issues that need to be addressed before merging. The code is functional and well-structured overall, but has import path problems that would prevent execution in certain configurations, missing conftest.py/pytest fixtures following standard patterns, unused imports, an incorrect type annotation, and the test file misidentified as an integration test when it uses only stubs.

Detailed Review

  1. Import path issue (blocking): In scenarios/ecs_basics_scenario.py, the import from ecs_wrapper import EcsWrapper will fail when run from the scenarios/ subdirectory since ecs_wrapper.py lives one level up at python/example_code/ecs/ecs_wrapper.py. Either a sys.path adjustment is needed or the file should be moved to the same directory as the wrapper. Similarly, test/test_ecs_integ.py uses sys.path.insert(0, ...) which only adds the test/ directory, then imports from ecs_wrapper import EcsWrapper — the path should point to the parent ecs/ folder, not the test/ folder itself (os.path.dirname(os.path.dirname(...))). This is a blocking runtime issue.

  2. Unused import: In ecs_wrapper.py, Optional is imported from typing but never used. This will trigger a pylint warning.

  3. Incorrect type annotation: In ecs_wrapper.py, def __init__(self, ecs_client: boto3.client)boto3.client is a function/factory, not a type. The correct annotation is boto3.client as a callable result, which should be typed as botocore.client.BaseClient or simply left untyped. This is a minor but recurring issue also present in comparable examples.

  4. Missing conftest.py: Premium reference examples (controltower, conditional_requests, etc.) all include a conftest.py in the test folder. This PR has no conftest.py, meaning there are no shared fixtures or proper sys.path setup following the repository standard pattern.

  5. Test file naming inconsistency: The file is named test_ecs_integ.py (integration) but the docstring and implementation use only Stubber/offline stubs — no actual AWS calls. It should be renamed test_ecs_wrapper.py or test_ecs_scenario.py to avoid confusion and match the unit/integration split used in comparable examples.

  6. No scenario-level test: There is no test for run_scenario() in ecs_basics_scenario.py itself (analogous to test_scenario_run.py in controltower). The tests only cover the wrapper methods. A scenario-level test using mocked/stubbed CloudFormation and ECS calls would be expected per the premium patterns.

  7. time.sleep(5) in cleanup (minor): In ecs_basics_scenario.py cleanup, there is a bare time.sleep(5) between scaling to 0 and deleting. This is fragile and unnecessary since delete_service with force=True handles this. Better to remove or use the waiter instead.

  8. list() constructor overuse: Throughout the code, list() is used as an empty list literal where [] is idiomatic Python (e.g., cluster_arns = list(), task_arns = list()). This doesn't affect functionality but deviates from PEP 8 style.

  9. Missing README.md: Comparable examples all include a README.md (see controltower, conditional_requests). The ECS example lacks one, which is typically required for the repository.

  10. Good aspects: The wrapper class is well-organized with proper paginator usage, waiter support (wait_for_service_stable/wait_for_service_inactive), correct CloudFormation-based prerequisite setup, comprehensive Stubber-based unit tests for all happy path and error scenarios, proper finally block for guaranteed cleanup in the scenario, and good docstrings throughout. The metadata YAML changes are correctly structured.


This review was generated automatically using Amazon Bedrock. It compares your changes against existing examples and coding guidelines. Please use your judgment — this is advisory, not authoritative.

from ecs_wrapper import EcsWrapper

logger = logging.getLogger(__name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This import will fail when the scenario is run from the scenarios/ subdirectory since ecs_wrapper.py is in the parent ecs/ directory. Add import sys, os; sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) before this import, or restructure the files so they are in the same directory.


import sys
import os

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) inserts the test/ directory itself, but ecs_wrapper.py is one level up in the ecs/ folder. This should be os.path.dirname(os.path.dirname(os.path.abspath(__file__))) to resolve the import correctly.

"""

import logging
from typing import Any, Dict, List, Optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Optional is imported but never used anywhere in this file. Remove it to avoid pylint warnings.


def __init__(self, ecs_client: boto3.client):
"""
Initializes the EcsWrapper with an ECS client.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 boto3.client is a factory function, not a type — using it as a type annotation is incorrect. Use Any from typing, or more precisely botocore.client.BaseClient. This is a typing correctness issue that mypy will flag.

# Step 9: Delete the ECS Service
try:
print(f"\n--- Cleanup: Scale down and delete service '{service_name}' ---")
try:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 time.sleep(5) is fragile and arbitrary. Since delete_service(..., force=True) is called immediately after, ECS will handle draining regardless. Consider removing the sleep entirely, or if a delay is intentional, add a comment explaining why and consider using the wait_for_service_inactive waiter instead.

try:
paginator = ecs_client.get_paginator("list_clusters")
cluster_arns = list()
for page in paginator.paginate():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cluster_arns = list() — per Python idioms and PEP 8, prefer cluster_arns = [] over the list() constructor for an empty list literal. This pattern recurs throughout the codebase.

Superficial change to test commit and push workflow.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Example Review

{
"summary": "This follow-up PR has addressed several previous issues (the time.sleep(5) now uses the wait_for_service_inactive waiter, list literals feedback is partially addressed) but two blocking issues from the previous review remain unresolved: the sys.path.insert in test_ecs_integ.py still inserts the wrong directory (test/ instead of ecs/), and the Optional import in ecs_wrapper.py is still unused. Additionally, the import path issue in ecs_basics_scenario.py for ecs_wrapper remains fragile when run from the scenarios/ subdirectory.",
"detailed_review": "1. [BLOCKING - UNRESOLVED] Wrong sys.path in test file (python/example_code/ecs/test/test_ecs_integ.py, line 11): sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) inserts the test/ directory, but ecs_wrapper.py lives one level up in ecs/. This means from ecs_wrapper import EcsWrapper will fail when running pytest from any directory other than ecs/test/. It should be os.path.dirname(os.path.dirname(os.path.abspath(__file__))). This was flagged in the previous review and is still unfixed. Comparable examples (e.g., controltower/test/conftest.py) use a conftest.py with the correct parent-directory path append.\n\n2. [BLOCKING - UNRESOLVED] Unused Optional import (python/example_code/ecs/ecs_wrapper.py, line 9): Optional is imported from typing but never used anywhere in the file. This will cause a pylint warning and should be removed. Flagged in previous review, still present.\n\n3. [BLOCKING - UNRESOLVED] Import fragility in scenario (python/example_code/ecs/scenarios/ecs_basics_scenario.py, line 32): from ecs_wrapper import EcsWrapper will fail when running the script directly from the scenarios/ subdirectory because ecs_wrapper.py is in the parent ecs/ directory. The previous review suggested adding a sys.path manipulation or restructuring files. Neither was done. This is a runability issue.\n\n4. [UNRESOLVED - Minor] list() constructor vs [] literal (ecs_wrapper.py and ecs_hello.py): The previous review noted that list() should be replaced with [] per PEP 8 / Python idiom. This pattern remains throughout: cluster_arns = list(), task_arns = list(), output_map = dict(), and list() as a .get() default. These should be [] and {} respectively.\n\n5. [ADDRESSED] time.sleep(5) + service delete pattern: The previous review flagged the time.sleep(5) as fragile. The code now calls update_service with desired_count=0, sleeps briefly (still 5 seconds), then delete_service(force=True) followed by wait_for_service_inactive. The waiter is now used, which is an improvement. The time.sleep(5) that remains after update_service is still somewhat arbitrary but is now less critical since the real wait is the waiter. Consider adding an inline comment explaining it.\n\n6. [ADDRESSED] boto3.client type annotation was flagged previously — the annotation ecs_client: boto3.client is still present in ecs_wrapper.py line 23. boto3.client is a factory function, not a type. This should be Any or botocore.client.BaseClient. This was flagged previously and remains unaddressed.\n\n7. [NEW ISSUE] Missing conftest.py in python/example_code/ecs/test/: The comparable premium examples (controltower, conditional_requests) all use a conftest.py to configure sys.path. This repo's pattern is to use conftest.py for path manipulation rather than inline sys.path.insert in test files. Adding a conftest.py would be cleaner and consistent.\n\n8. [NEW ISSUE] No README.md for the ECS example: Other examples in the repo include a README.md describing prerequisites, usage, and what is demonstrated. The ECS directory is missing one.\n\n9. [GOOD] Test coverage: The test file is comprehensive — it covers all wrapper methods with both success and error cases using botocore Stubber. The stubs are correctly structured with expected params and responses. This is solid work.\n\n10. [GOOD] CloudFormation-based prerequisite provisioning: Using CloudFormation to provision VPC/subnets/IAM role is a best practice that avoids hardcoded infrastructure values and ensures clean teardown. The template is well-structured and the waiter-based polling is correct.",
"inline_comments": [
{
"path": "python/example_code/ecs/test/test_ecs_integ.py",
"line": 11,
"body": "[UNRESOLVED from previous review] This inserts the test/ directory itself into sys.path, but ecs_wrapper.py lives in the parent ecs/ directory. Change to:\n


This review was generated automatically using Amazon Bedrock. It compares your changes against existing examples and coding guidelines. Please use your judgment — this is advisory, not authoritative.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Example Review

This follow-up PR addresses several previous review issues but two blockers remain: the sys.path fix in the test file still inserts the wrong directory (test/ instead of the parent ecs/), causing the from ecs_wrapper import EcsWrapper import to fail at runtime; and the scenario file's from ecs_wrapper import EcsWrapper import will still fail when run from the scenarios/ subdirectory. The Optional import and the list() constructor issues noted previously have not been addressed. The code is otherwise well-structured with good test coverage, proper waiter usage, and solid error handling.

Detailed Review

  1. [BLOCKER] Test import path is still wrong (test_ecs_integ.py:11): sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) inserts the test/ directory itself into the path. Since ecs_wrapper.py lives one level up in ecs/, the from ecs_wrapper import EcsWrapper import on line 17 will raise ModuleNotFoundError when tests are run from the test/ directory. This should be os.path.dirname(os.path.dirname(os.path.abspath(__file__))) — identical to the feedback in the previous review and still unresolved.

  2. [BLOCKER] Scenario import still broken (ecs_basics_scenario.py:32): from ecs_wrapper import EcsWrapper will fail when the script is executed from the scenarios/ subdirectory because ecs_wrapper.py is in the parent ecs/ directory. A conftest.py or explicit sys.path manipulation before the import (as recommended in the prior review) is still absent. This is the same issue raised before, unaddressed.

  3. [REMAINING] Optional still imported but unused (ecs_wrapper.py:9): from typing import Any, Dict, List, OptionalOptional is never used anywhere in the file. This was flagged in the previous review and is unchanged, which will generate a pylint/mypy warning.

  4. [REMAINING] list() constructor instead of [] (ecs_hello.py:20, ecs_wrapper.py throughout): Multiple occurrences of cluster_arns = list(), task_arns = list(), output_map = dict(), etc. PEP 8 and the previous review both recommend using [] and {} literals. These are still present.

  5. [REMAINING] boto3.client as a type annotation (ecs_wrapper.py:23): The __init__ parameter ecs_client: boto3.client uses boto3.client (a factory function) as a type annotation. This is incorrect and mypy will flag it. The previous review suggested using Any or botocore.client.BaseClient. Still unchanged.

  6. [GOOD] Waiter usage is correct and the previous time.sleep(5) issue has been partially addressed: The scenario now properly uses wait_for_service_inactive after delete_service. However, the time.sleep(5) call between update_service(desired_count=0) and delete_service(..., force=True) on cleanup (ecs_basics_scenario.py ~358) is still present and still lacks an explanatory comment. Since force=True is used, this sleep is unnecessary.

  7. [GOOD] Comprehensive test coverage: The test file covers all 10+ wrapper methods with both success and error paths, using the botocore Stubber correctly. The stubbed responses match the expected API shapes. This is a strong positive.

  8. [GOOD] CloudFormation-based infrastructure setup: Using a CFN stack to provision VPC, subnets, security group, and IAM role is a clean approach — it avoids hardcoded resource IDs, supports teardown, and is reusable.

  9. [GOOD] Error handling pattern is consistent: All wrapper methods follow the same pattern of catching ClientError, logging a specific message for known error codes, and re-raising. This is aligned with SDK example best practices.

  10. [MINOR] No conftest.py for tests: Premium comparable examples (e.g., controltower, conditional_requests) use a conftest.py in the test/ directory to handle path setup and shared fixtures. Adding a conftest.py with proper sys.path configuration would be the idiomatic pattern and would fix the import issue cleanly rather than using an inline sys.path.insert in the test file itself.


This review was generated automatically using Amazon Bedrock. It compares your changes against existing examples and coding guidelines. Please use your judgment — this is advisory, not authoritative.


import sys
import os

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This still inserts the test/ directory itself, not the ecs/ parent where ecs_wrapper.py lives. Change to os.path.dirname(os.path.dirname(os.path.abspath(__file__))) so that from ecs_wrapper import EcsWrapper resolves correctly. This was flagged in the previous review and remains a blocking issue.

from ecs_wrapper import EcsWrapper

logger = logging.getLogger(__name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 from ecs_wrapper import EcsWrapper will raise ModuleNotFoundError when this script is run from the scenarios/ subdirectory. Add import sys, os; sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) before this import, or move both files to the same directory. This was flagged in the previous review and is still unresolved.

"""

import logging
from typing import Any, Dict, List, Optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Optional is imported but never used anywhere in this file. Remove it to avoid pylint/mypy warnings. Flagged in the previous review; still present.


def __init__(self, ecs_client: boto3.client):
"""
Initializes the EcsWrapper with an ECS client.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 boto3.client is a factory function, not a type — using it as ecs_client: boto3.client is incorrect and mypy will flag it. Use Any from typing or botocore.client.BaseClient instead. This was flagged in the previous review and remains unchanged.

ecs_client = boto3.client("ecs")

try:
paginator = ecs_client.get_paginator("list_clusters")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cluster_arns = list() — prefer the list literal cluster_arns = [] per PEP 8. Same pattern appears throughout ecs_wrapper.py (e.g., task_arns = list(), output_map = dict()). Flagged in the previous review; still present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Python This issue relates to the AWS SDK for Python (boto3)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants