awsquery 1.0.0__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {awsquery-1.0.0 → awsquery-1.2.0}/.github/workflows/quality.yml +5 -1
- {awsquery-1.0.0 → awsquery-1.2.0}/.gitignore +8 -1
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/CONTEXT.md +79 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/EXECUTION.md +147 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/ISSUE.md +72 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/PLAN.html +320 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/PLAN.md +773 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/RESEARCH.html +382 -0
- awsquery-1.2.0/.issues/ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action/RESEARCH.md +625 -0
- awsquery-1.2.0/AGENTS.md +62 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/Makefile +35 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/PKG-INFO +446 -18
- {awsquery-1.0.0 → awsquery-1.2.0}/README.md +440 -12
- awsquery-1.2.0/llms-full.txt +390 -0
- awsquery-1.2.0/llms.txt +21 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/pyproject.toml +13 -7
- awsquery-1.2.0/src/awsquery/agent-reference.md +390 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/cli.py +496 -211
- awsquery-1.2.0/src/awsquery/config.py +160 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/core.py +84 -64
- awsquery-1.2.0/src/awsquery/data_fields.yaml +41 -0
- awsquery-1.2.0/src/awsquery/default_actions.yaml +54 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/default_filters.yaml +351 -602
- awsquery-1.2.0/src/awsquery/errors.py +245 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/filter_validator.py +2 -2
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/formatters.py +191 -99
- awsquery-1.2.0/src/awsquery/introspection.py +222 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/security.py +34 -2
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/shapes.py +170 -28
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/utils.py +8 -1
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/PKG-INFO +446 -18
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/SOURCES.txt +19 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/conftest.py +18 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_end_to_end.py +28 -17
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_feature_combinations.py +37 -31
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_keys_behavior.py +30 -12
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_multi_level.py +4 -4
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_session_management.py +69 -4
- awsquery-1.2.0/tests/unit/test_agent_reference.py +201 -0
- awsquery-1.2.0/tests/unit/test_cli_flags.py +1197 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_cli_help_output.py +28 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_cli_parser.py +78 -1
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_cli_session_args.py +3 -1
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_config_loading_simple.py +34 -2
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_core.py +116 -8
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_default_column_filters.py +319 -7
- awsquery-1.2.0/tests/unit/test_errors.py +500 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_formatters.py +562 -23
- awsquery-1.2.0/tests/unit/test_introspection.py +393 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_keys_display_fix.py +4 -8
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_security.py +14 -5
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_security_edge_cases.py +2 -1
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_shapes.py +209 -10
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_tags_transformation.py +5 -8
- awsquery-1.2.0/uv.lock +3395 -0
- awsquery-1.0.0/src/awsquery/config.py +0 -80
- awsquery-1.0.0/tests/unit/test_cli_flags.py +0 -279
- {awsquery-1.0.0 → awsquery-1.2.0}/.claude/agents/code-reviewer.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.claude/agents/makefile-optimizer.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.claude/agents/python-infra-automator.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.claude/agents/test-writer.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.claude/settings.local.json +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.coveragerc +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.devcontainer/devcontainer.json +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.dockerignore +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.flake8 +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.github/workflows/release.yml +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.github/workflows/test.yml +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/e1bzl-fix-remaining-15-broken-suffix-entries-surfaced-by-audit/EXECUTION.html +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/e1bzl-fix-remaining-15-broken-suffix-entries-surfaced-by-audit/EXECUTION.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/e1bzl-fix-remaining-15-broken-suffix-entries-surfaced-by-audit/ISSUE.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/e1bzl-fix-remaining-15-broken-suffix-entries-surfaced-by-audit/PLAN.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/CONTEXT.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/EXECUTION.html +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/EXECUTION.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/ISSUE.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/PLAN.html +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/PLAN.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/RESEARCH.html +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/RESEARCH.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/research/codebase.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/research/ecosystem.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/.issues/archive/sue5t-fix-tags-default-filters-and-add-additive-column-syntax/research/pitfalls.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/CLAUDE.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/Dockerfile +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/Dockerfile.prod +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/LICENSE +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/compose.yaml +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/coverage.json +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/debug-output.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/docs/DOCKER.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/docs/mutation-testing.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/policy.json +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/pytest.ini +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/requirements.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/scripts/README.md +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/scripts/audit_default_filters.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/scripts/validate-awsquery.sh +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/setup.cfg +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/__init__.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/auto_filters.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/case_utils.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/filters.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery/policy.json +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/dependency_links.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/entry_points.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/requires.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/src/awsquery.egg-info/top_level.txt +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/__init__.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/fixtures/__init__.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/fixtures/aws_responses.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/fixtures/policy_samples.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/__init__.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_autocomplete_resilience.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_multi_level_calls.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_parameter_option_integration.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_parameter_type_auto_wrapping.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/integration/test_shape_aware_integration.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/__init__.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_auto_filters.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_case_utils.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_cli_arg_processing.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_column_ordering.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_core_inference.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_core_string_handling.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_critical_mutations.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_cross_service_hints.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_filter_edge_cases.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_filter_implementation.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_filter_parameter_extraction.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_filter_validator.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_formatter_edge_cases.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_formatters_data_processing.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_input_hint_parameter.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_messaging_enhancements.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_parameter_option.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_parameter_type_validation.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_preemptive_multi_step.py +0 -0
- {awsquery-1.0.0 → awsquery-1.2.0}/tests/unit/test_smart_prefix_matching.py +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# CONTEXT — ty5dx
|
|
2
|
+
|
|
3
|
+
Design decisions captured before research/planning. The researcher and planner
|
|
4
|
+
must treat the "Decisions" section as locked.
|
|
5
|
+
|
|
6
|
+
## Decisions (locked — research/planner must follow)
|
|
7
|
+
|
|
8
|
+
- **Config location: a new `src/awsquery/default_actions.yaml`**, flat
|
|
9
|
+
`service: action` map (`ec2: describe_instances`). Not a key inside
|
|
10
|
+
`default_filters.yaml`.
|
|
11
|
+
**Why:** two walkers assume the strict `service -> action -> {columns: [...]}`
|
|
12
|
+
shape — `scripts/audit_default_filters.py:71` (`opcfg.get("columns", [])`) and
|
|
13
|
+
`tests/unit/test_default_column_filters.py:353`. A sibling string key would
|
|
14
|
+
raise `AttributeError: 'str' object has no attribute 'get'` in both. A separate
|
|
15
|
+
file leaves every existing walker untouched.
|
|
16
|
+
Must be added to the package data list in `pyproject.toml:82` alongside
|
|
17
|
+
`policy.json` and `default_filters.yaml`, or it will not ship in the wheel.
|
|
18
|
+
|
|
19
|
+
- **Loader mirrors the existing config module.** Add `load_default_actions()`
|
|
20
|
+
(`@lru_cache(maxsize=1)`, same FileNotFoundError / YAMLError / generic
|
|
21
|
+
fallbacks returning `{}`) and `get_default_action(service)` to
|
|
22
|
+
`src/awsquery/config.py`, shaped exactly like `load_default_filters()` /
|
|
23
|
+
`get_default_columns()`: lowercased lookup, `debug_print` on both hit and
|
|
24
|
+
miss, `None` when unset.
|
|
25
|
+
|
|
26
|
+
- **Action names only — no default parameters.** The map value is the operation
|
|
27
|
+
name and nothing else. Output shaping stays with `default_filters.yaml`
|
|
28
|
+
columns.
|
|
29
|
+
|
|
30
|
+
- **Defaults are restricted to operations with no required parameters.** A
|
|
31
|
+
service whose natural listing needs an argument (e.g. `logs
|
|
32
|
+
describe-log-streams` needs `LogGroupName`) gets no default at all rather
|
|
33
|
+
than a default that fans out. `awsquery <service>` must always be a single
|
|
34
|
+
immediate call — no multi-level parameter resolution triggered by a default.
|
|
35
|
+
|
|
36
|
+
- **Coverage: broad sweep (~50+ services).** Cover every service already in
|
|
37
|
+
`default_filters.yaml` that has an obvious parameterless list/describe entry
|
|
38
|
+
point. Skip any service where the entry point needs parameters (per the rule
|
|
39
|
+
above) or where no single operation is clearly "the" default.
|
|
40
|
+
|
|
41
|
+
- **Announcement: debug only.** A `debug_print` records which default was
|
|
42
|
+
chosen. No hint on stderr during normal runs — stdout and stderr stay clean
|
|
43
|
+
for piping.
|
|
44
|
+
|
|
45
|
+
- **Fallback when a service has no default:** print that service's available
|
|
46
|
+
actions to stderr, exit non-zero. `awsquery` with no service at all keeps
|
|
47
|
+
today's behavior (list services on stdout, exit 0).
|
|
48
|
+
|
|
49
|
+
- **Resolved action takes the normal code path.** Once substituted it flows
|
|
50
|
+
through `validate_readonly`, default column filters, and any user `--`
|
|
51
|
+
filters/flags unchanged. `awsquery ec2 -- InstanceId` must equal
|
|
52
|
+
`awsquery ec2 describe-instances -- InstanceId`.
|
|
53
|
+
|
|
54
|
+
## Claude's Discretion (research should explore options)
|
|
55
|
+
|
|
56
|
+
- Exact insertion point in `main()` for the substitution — the current dead end
|
|
57
|
+
is the `if not args.service or not args.action` branch at
|
|
58
|
+
`src/awsquery/cli.py:830`, but `_build_filter_argv` (`cli.py:303`) also
|
|
59
|
+
branches on `args.action` and may need the resolved value.
|
|
60
|
+
- Whether the fallback action listing reuses `get_service_operations()` /
|
|
61
|
+
`get_service_valid_operations()` (already in `cli.py`, used by
|
|
62
|
+
`action_completer`) or needs a shared helper.
|
|
63
|
+
- Naming: `get_default_action` vs. something clearer; the surrounding module
|
|
64
|
+
favours `get_default_*`.
|
|
65
|
+
- How the curated map is validated: a test asserting every entry is a real
|
|
66
|
+
botocore operation, is ReadOnly per `policy.json`, and has no required
|
|
67
|
+
parameters is the obvious guard — shape and location are the researcher's
|
|
68
|
+
call.
|
|
69
|
+
- Whether kebab-case (`describe-instances`) or snake_case
|
|
70
|
+
(`describe_instances`) is the canonical value in the YAML;
|
|
71
|
+
`normalize_action_name` (`utils.py`) already exists for conversion.
|
|
72
|
+
|
|
73
|
+
## Deferred (out of scope for this issue)
|
|
74
|
+
|
|
75
|
+
- Botocore heuristic inference for services with no curated entry — curated map
|
|
76
|
+
only in this issue.
|
|
77
|
+
- Default parameters (`MaxResults`, filters) attached to a default action.
|
|
78
|
+
- User-level override of the curated map (`~/.awsquery/` config).
|
|
79
|
+
- Refreshing the stale `.issues/MAP.md` (99 days old at time of writing).
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Execution: Add per-service default actions so `awsquery ec2` runs without an action
|
|
2
|
+
|
|
3
|
+
**Started:** 2026-09-08T13:11:12Z
|
|
4
|
+
**Status:** complete
|
|
5
|
+
**Branch:** ty5dx-add-per-service-default-actions-so-awsquery-ec2-runs-without-an-action
|
|
6
|
+
|
|
7
|
+
## Baseline
|
|
8
|
+
|
|
9
|
+
`python3 -m pytest tests/ -q` before any changes: 1346 passed.
|
|
10
|
+
|
|
11
|
+
## Execution Log
|
|
12
|
+
|
|
13
|
+
- [x] Task 1: Add the curated default_actions.yaml and register it for packaging — commit 1366550
|
|
14
|
+
- [x] Task 2: Add load_default_actions() and get_default_action() to config.py — commit 71056cf
|
|
15
|
+
- [x] Task 3: Pre-parse argv injection in cli.py plus the no-default stderr fallback — commit e440aea
|
|
16
|
+
- Deviation: [Rule 1 - Bug] `test_argparse_system_exit_handling` in
|
|
17
|
+
tests/integration/test_end_to_end.py asserted the old dead-end behavior (unrecognized
|
|
18
|
+
`--unknown-option value` argv left `args.service='value'`, `args.action=None`, and the old
|
|
19
|
+
combined branch printed the service list and exited 0). The new split branch correctly
|
|
20
|
+
treats a service with no action as "no default configured", prints that service's actions
|
|
21
|
+
on stderr, and exits 1 for the now-nonsensical positional `value`. Changed the test's argv
|
|
22
|
+
to `["awsquery", "--unknown-option"]` (no stray positional) so it still exercises the
|
|
23
|
+
SystemExit(0)-with-unrecognized-flag path it was written for, without asserting on the
|
|
24
|
+
accidental "value"-as-service artifact. Included in commit e440aea.
|
|
25
|
+
- [x] Task 4: Curated-map validation, injector, flag pass-through and casing tests — commit 8055ead
|
|
26
|
+
- Deviation: [Rule 1 - Bug] `_inject_default_action`'s `debug_print("Using default action...")`
|
|
27
|
+
was silently swallowed on every real invocation: it runs during the *first*
|
|
28
|
+
`parser.parse_known_args()` call, before `utils.set_debug_enabled(args.debug)` is reached
|
|
29
|
+
later in `main()`, so the global debug flag was still `False` at the moment the injector's
|
|
30
|
+
debug_print executed — the line could never appear on stderr for `awsquery -d ec2`,
|
|
31
|
+
contradicting Task 4's own required test ("`-d ec2` emits the `Using default action` line
|
|
32
|
+
on stderr") and the plan's success criterion that a debug_print announces the chosen
|
|
33
|
+
default. Fixed by pre-enabling debug mode from a lightweight scan of raw `sys.argv[1:]`
|
|
34
|
+
for `-d`/`--debug` immediately before the injection call; the later
|
|
35
|
+
`utils.set_debug_enabled(args.debug)` (now with its redundant `from . import utils`
|
|
36
|
+
removed) still re-applies the authoritative parsed value. Included in commit 8055ead.
|
|
37
|
+
- [x] Task 5: Rewrite the vacuously-green integration test at test_end_to_end.py:889 — commit ff45e3b
|
|
38
|
+
- [x] Task 6: Document the default-action shortcut in the README — commit 848bb9c
|
|
39
|
+
|
|
40
|
+
## Verification Results
|
|
41
|
+
|
|
42
|
+
**`make test`:** 1420 passed (baseline before this issue: 1346 passed; net +74 new/rewritten
|
|
43
|
+
tests across the six tasks). Full output tail:
|
|
44
|
+
```
|
|
45
|
+
============================ 1420 passed in 11.76s =============================
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**`make lint`:** clean.
|
|
49
|
+
```
|
|
50
|
+
flake8 src/ tests/ --count --statistics --show-source
|
|
51
|
+
0
|
|
52
|
+
pylint src/awsquery
|
|
53
|
+
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**`make format-check`:** clean.
|
|
57
|
+
```
|
|
58
|
+
black --check --diff src/ tests/
|
|
59
|
+
All done! (62 files would be left unchanged)
|
|
60
|
+
isort --check-only --diff src/ tests/
|
|
61
|
+
(no output — clean)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Coverage gate:** `pytest.ini`/`Makefile` do not actually pass `--cov-fail-under` to `make
|
|
65
|
+
test` (coverage is opt-in via `make coverage`); nothing to fail on. `make test` completed with
|
|
66
|
+
0 non-coverage failures either way.
|
|
67
|
+
|
|
68
|
+
**Packaging spot-check** (plan-mandated, not coverable by unit tests):
|
|
69
|
+
```
|
|
70
|
+
python3 -m build
|
|
71
|
+
unzip -l dist/*.whl | grep default_actions.yaml
|
|
72
|
+
1604 ... awsquery/default_actions.yaml
|
|
73
|
+
```
|
|
74
|
+
Confirms `default_actions.yaml` ships inside the built wheel. `dist/`, `build/`, and
|
|
75
|
+
`src/awsquery.egg-info/` were removed after the check (build artifacts, not part of the repo).
|
|
76
|
+
|
|
77
|
+
**Manual injector spot-check** (matches README examples):
|
|
78
|
+
```
|
|
79
|
+
_inject_default_action(['ec2']) == ['ec2', 'describe-instances']
|
|
80
|
+
_inject_default_action(['s3', '--', 'Name']) == ['s3', 'list-buckets', '--', 'Name']
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Deviations from Plan
|
|
84
|
+
|
|
85
|
+
### Auto-fixed (Rules 1-3)
|
|
86
|
+
|
|
87
|
+
1. **[Rule 1 - Bug] `test_argparse_system_exit_handling` asserted the old dead-end branch
|
|
88
|
+
behavior** (Task 3, commit e440aea)
|
|
89
|
+
- Found during: Task 3 full-suite verification (`python3 -m pytest tests/ -q`)
|
|
90
|
+
- Issue: `sys.argv = ["awsquery", "--unknown-option", "value"]` left argparse assigning
|
|
91
|
+
`args.service = "value"` (positional match) and `args.action = None`. Before this issue,
|
|
92
|
+
the combined `if not args.service or not args.action:` branch printed the service list and
|
|
93
|
+
exited 0 for this case — the test asserted `exit code == 0`. After splitting the branch
|
|
94
|
+
(per Task 3's own spec), a service with no action but no configured default now correctly
|
|
95
|
+
prints that service's actions on stderr and exits 1, so `"value"` (not a real service)
|
|
96
|
+
produced `ERROR: Unknown service 'value'` and exit 1, failing the old assertion.
|
|
97
|
+
- Fix: Changed the test's argv to `["awsquery", "--unknown-option"]` (no stray positional),
|
|
98
|
+
which still leaves `args.service = None` and exercises the same "unrecognized flag doesn't
|
|
99
|
+
crash the parser, still exits 0" intent the test was written for, without depending on the
|
|
100
|
+
accidental "value"-as-service artifact that the new behavior correctly stops tolerating.
|
|
101
|
+
- Files: `tests/integration/test_end_to_end.py`
|
|
102
|
+
|
|
103
|
+
2. **[Rule 1 - Bug] `_inject_default_action`'s debug_print was silently swallowed on every real
|
|
104
|
+
invocation** (Task 4, commit 8055ead)
|
|
105
|
+
- Found during: Task 4, writing the required test that `-d ec2` emits the `Using default
|
|
106
|
+
action` line on stderr
|
|
107
|
+
- Issue: the injector's `debug_print(...)` executes during the *first*
|
|
108
|
+
`parser.parse_known_args()` call, which is chronologically before
|
|
109
|
+
`utils.set_debug_enabled(args.debug)` runs later in `main()`. The global debug flag was
|
|
110
|
+
therefore still `False` at the moment the injector's debug_print fired, so the line could
|
|
111
|
+
never appear on stderr for `awsquery -d ec2` — contradicting both Task 4's required test
|
|
112
|
+
and the plan's stated success criterion ("Only a debug_print announces the chosen
|
|
113
|
+
default").
|
|
114
|
+
- Fix: pre-enable debug mode from a lightweight scan of raw `sys.argv[1:]` for `-d`/`--debug`
|
|
115
|
+
immediately before the injection call; the later `utils.set_debug_enabled(args.debug)`
|
|
116
|
+
(with its now-redundant `from . import utils` removed) still re-applies the authoritative
|
|
117
|
+
parsed value, so final debug state is unaffected — only the injector's own debug line was
|
|
118
|
+
previously lost.
|
|
119
|
+
- Files: `src/awsquery/cli.py`
|
|
120
|
+
|
|
121
|
+
### Blocked (Rule 4)
|
|
122
|
+
|
|
123
|
+
None.
|
|
124
|
+
|
|
125
|
+
## Discovered Issues
|
|
126
|
+
|
|
127
|
+
None found outside the scope of this issue.
|
|
128
|
+
|
|
129
|
+
## Self-Check
|
|
130
|
+
|
|
131
|
+
- [x] All files from plan exist (verified: `src/awsquery/default_actions.yaml`,
|
|
132
|
+
`src/awsquery/config.py`, `src/awsquery/cli.py`, `pyproject.toml`, all five test files,
|
|
133
|
+
`README.md`)
|
|
134
|
+
- [x] All 6 commit hashes exist on branch (`git log --oneline` confirmed:
|
|
135
|
+
1366550, 71056cf, e440aea, 8055ead, ff45e3b, 848bb9c)
|
|
136
|
+
- [x] Full verification suite passes for real: `make test` (1420 passed), `make lint` (clean),
|
|
137
|
+
`make format-check` (clean), plus the plan's packaging spot-check
|
|
138
|
+
- [x] No stubs/TODOs/placeholders introduced (`grep -iE "TODO|FIXME|HACK|XXX|PLACEHOLDER|coming
|
|
139
|
+
soon|not implemented"` over the diff: no matches)
|
|
140
|
+
- [x] No leftover debug code (`grep` over the diff for `console.log`/`debugger`/`binding.pry`/
|
|
141
|
+
`breakpoint()` bare prints: no matches; all `print()`/`debug_print()` calls are intentional
|
|
142
|
+
CLI output or the existing debug-logging convention)
|
|
143
|
+
- **Result:** PASSED
|
|
144
|
+
|
|
145
|
+
**Completed:** 2026-09-08T13:21:59Z
|
|
146
|
+
**Duration:** ~11 minutes
|
|
147
|
+
**Commits:** 6
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ty5dx
|
|
3
|
+
title: Add per-service default actions so awsquery ec2 runs without an action
|
|
4
|
+
status: done
|
|
5
|
+
ship_state: pr_open
|
|
6
|
+
priority: medium
|
|
7
|
+
labels:
|
|
8
|
+
- enhancement
|
|
9
|
+
- cli
|
|
10
|
+
remote:
|
|
11
|
+
- source: github
|
|
12
|
+
id: '18'
|
|
13
|
+
url: https://github.com/flomotlik/awsquery/issues/18
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
Today `awsquery ec2` (service given, no action) falls into the `if not args.service or not args.action` branch in `src/awsquery/cli.py:830` and prints the full list of AWS services — which ignores the service the user just typed and is not useful.
|
|
19
|
+
|
|
20
|
+
`awsinfo` treats the bare service name as a shortcut for its most common read operation. We want the same: `awsquery ec2` should behave as `awsquery ec2 describe-instances`, `awsquery s3` as `awsquery s3 list-buckets`, and so on.
|
|
21
|
+
|
|
22
|
+
The pieces to build on already exist: `src/awsquery/config.py` loads and caches `src/awsquery/default_filters.yaml` (`load_default_filters`), and `get_default_columns(service, action)` already resolves per-service/per-action output columns from it. A default *action* is the missing counterpart to the default *columns*.
|
|
23
|
+
|
|
24
|
+
## Scope
|
|
25
|
+
|
|
26
|
+
**1. Curated YAML map for default actions**
|
|
27
|
+
|
|
28
|
+
- Add a per-service `default_action:` key to the existing `default_filters.yaml` structure (sibling of the per-action entries), e.g.:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
ec2:
|
|
32
|
+
default_action: describe_instances
|
|
33
|
+
describe_instances:
|
|
34
|
+
columns:
|
|
35
|
+
- InstanceId$
|
|
36
|
+
...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Hand-curate the mapping for the common services (ec2, s3, rds, lambda, ecs, eks, iam, cloudformation, dynamodb, sqs, sns, elbv2, autoscaling, cloudwatch, logs, ssm, secretsmanager, route53, acm, …). Curated only — no botocore heuristic inference in this issue.
|
|
40
|
+
- Add a `get_default_action(service)` accessor in `src/awsquery/config.py`, matching the existing `get_default_columns` shape (normalized/lowercased lookup, `debug_print` on hit and miss, returns `None` when unset).
|
|
41
|
+
|
|
42
|
+
**2. CLI wiring**
|
|
43
|
+
|
|
44
|
+
- In `main()` (`src/awsquery/cli.py`), when `args.service` is set and `args.action` is empty, resolve the default action via `get_default_action(service)` and continue as if the user had typed it.
|
|
45
|
+
- The resolved action must go through the normal path unchanged: `validate_readonly` security validation, parameter requirements / multi-level resolution, default column filters, and any user-supplied `--` filters and flags. `awsquery ec2 -- InstanceId` must work exactly like `awsquery ec2 describe-instances -- InstanceId`.
|
|
46
|
+
- Emit a `debug_print` recording which default action was chosen.
|
|
47
|
+
- The default action name carries **no default parameters** — it is only the operation name. Output shaping stays with the existing `default_filters.yaml` columns.
|
|
48
|
+
|
|
49
|
+
**3. Fallback when no default is configured**
|
|
50
|
+
|
|
51
|
+
- `awsquery <service>` for a service with no `default_action:` prints the available actions **for that service** to stderr and exits non-zero — replacing today's full-service-list behavior for this case.
|
|
52
|
+
- `awsquery` with no service at all keeps its current behavior (print available services, exit 0).
|
|
53
|
+
|
|
54
|
+
**4. Docs and tests**
|
|
55
|
+
|
|
56
|
+
- Update README/help text to document the shortcut and how to configure `default_action:`.
|
|
57
|
+
- Tests exercise the real implementation with only boto3 mocked, per the repo's testing rules — no pytest markers, no over-mocking.
|
|
58
|
+
|
|
59
|
+
## Acceptance Criteria
|
|
60
|
+
|
|
61
|
+
- [ ] `default_filters.yaml` carries a `default_action:` entry for each curated service, and the file still parses cleanly via `load_default_filters()`
|
|
62
|
+
- [ ] `config.get_default_action(service)` returns the configured action, is case/format-insensitive in the same way as `get_default_columns`, and returns `None` for unconfigured services
|
|
63
|
+
- [ ] `awsquery ec2` produces the same output as `awsquery ec2 describe-instances`
|
|
64
|
+
- [ ] `awsquery s3` produces the same output as `awsquery s3 list-buckets`
|
|
65
|
+
- [ ] User filters and flags apply to the defaulted action (`awsquery ec2 -- InstanceId` == `awsquery ec2 describe-instances -- InstanceId`)
|
|
66
|
+
- [ ] The defaulted action passes through `validate_readonly` — a non-ReadOnly default would be rejected like any other action
|
|
67
|
+
- [ ] Default column filters from `default_filters.yaml` still apply to the defaulted action
|
|
68
|
+
- [ ] `awsquery <service-without-default>` lists that service's actions on stderr and exits non-zero
|
|
69
|
+
- [ ] `awsquery` with no arguments still lists available services and exits 0
|
|
70
|
+
- [ ] Tests cover: default resolution, unconfigured-service fallback, filter/flag pass-through, and readonly validation of the defaulted action
|
|
71
|
+
- [ ] README documents the shortcut and the `default_action:` config key
|
|
72
|
+
- [ ] `make test`, `make lint`, and `make format-check` pass
|