Skip to content

Commit 614139a

Browse files
authored
Add packages option to run-ailly for Lliam (#187)
* Add packages option to run-ailly * Add better error handling to Lliam
1 parent e22a523 commit 614139a

File tree

8 files changed

+349
-38
lines changed

8 files changed

+349
-38
lines changed

aws_doc_sdk_examples_tools/lliam/domain/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class CreatePrompts(Command):
1717
@dataclass
1818
class RunAilly(Command):
1919
batches: List[str]
20+
packages: List[str]
2021

2122

2223
@dataclass
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class DomainError:
6+
pass
7+
8+
9+
@dataclass
10+
class CommandExecutionError(DomainError):
11+
command_name: str
12+
message: str

aws_doc_sdk_examples_tools/lliam/entry_points/lliam_app.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55
import logging
66
import typer
77

8-
from aws_doc_sdk_examples_tools.lliam.config import (
9-
AILLY_DIR,
10-
BATCH_PREFIX
11-
)
12-
from aws_doc_sdk_examples_tools.lliam.domain import commands
8+
from aws_doc_sdk_examples_tools.lliam.config import AILLY_DIR, BATCH_PREFIX
9+
from aws_doc_sdk_examples_tools.lliam.domain import commands, errors
1310
from aws_doc_sdk_examples_tools.lliam.service_layer import messagebus, unit_of_work
1411

1512
logging.basicConfig(
@@ -31,36 +28,39 @@ def create_prompts(iam_tributary_root: str, system_prompts: List[str] = []):
3128
out_dir=AILLY_DIR,
3229
)
3330
uow = unit_of_work.FsUnitOfWork()
34-
messagebus.handle(cmd, uow)
31+
errors = messagebus.handle(cmd, uow)
32+
handle_domain_errors(errors)
3533

3634

3735
@app.command()
3836
def run_ailly(
3937
batches: Annotated[
4038
Optional[str],
41-
typer.Option(
42-
help="Batch names to process (comma-separated list)"
43-
),
39+
typer.Option(help="Batch names to process (comma-separated list)"),
40+
] = None,
41+
packages: Annotated[
42+
Optional[str], typer.Option(help="Comma delimited list of packages to update")
4443
] = None,
4544
) -> None:
4645
"""
4746
Run ailly to generate IAM policy content and process the results.
4847
If batches is specified, only those batches will be processed.
4948
If batches is omitted, all batches will be processed.
49+
If packages is specified, only those packages will be processed.
5050
"""
5151
requested_batches = parse_batch_names(batches)
52-
cmd = commands.RunAilly(batches=requested_batches)
53-
messagebus.handle(cmd)
52+
package_names = parse_package_names(packages)
53+
cmd = commands.RunAilly(batches=requested_batches, packages=package_names)
54+
errors = messagebus.handle(cmd)
55+
handle_domain_errors(errors)
5456

5557

5658
@app.command()
5759
def update_reservoir(
5860
iam_tributary_root: str,
5961
batches: Annotated[
6062
Optional[str],
61-
typer.Option(
62-
help="Batch names to process (comma-separated list)"
63-
),
63+
typer.Option(help="Batch names to process (comma-separated list)"),
6464
] = None,
6565
packages: Annotated[
6666
Optional[str], typer.Option(help="Comma delimited list of packages to update")
@@ -77,7 +77,15 @@ def update_reservoir(
7777
cmd = commands.UpdateReservoir(
7878
root=doc_gen_root, batches=batch_names, packages=package_names
7979
)
80-
messagebus.handle(cmd)
80+
errors = messagebus.handle(cmd)
81+
handle_domain_errors(errors)
82+
83+
84+
def handle_domain_errors(errors: List[errors.DomainError]):
85+
if errors:
86+
for error in errors:
87+
logger.error(error)
88+
typer.Exit(code=1)
8189

8290

8391
def parse_batch_names(batch_names_str: Optional[str]) -> List[str]:
@@ -86,7 +94,7 @@ def parse_batch_names(batch_names_str: Optional[str]) -> List[str]:
8694
"""
8795
if not batch_names_str:
8896
return []
89-
97+
9098
batch_names = []
9199

92100
for name in batch_names_str.split(","):

aws_doc_sdk_examples_tools/lliam/service_layer/messagebus.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,21 @@
1212
Message = commands.Command
1313

1414

15-
def handle(
16-
message: commands.Command, uow: Optional[unit_of_work.FsUnitOfWork] = None
17-
):
15+
def handle(message: commands.Command, uow: Optional[unit_of_work.FsUnitOfWork] = None):
1816
queue = [message]
1917

2018
while queue:
2119
message = queue.pop(0)
2220
if isinstance(message, commands.Command):
23-
handle_command(message, uow)
21+
return handle_command(message, uow)
2422
else:
2523
raise Exception(f"{message} was not a Command")
2624

2725

28-
def handle_command(
29-
command: commands.Command, uow: Optional[unit_of_work.FsUnitOfWork]
30-
):
26+
def handle_command(command: commands.Command, uow: Optional[unit_of_work.FsUnitOfWork]):
3127
handler = COMMAND_HANDLERS[type(command)]
32-
handler(command, uow)
28+
errors = handler(command, uow)
29+
return errors
3330

3431

3532
COMMAND_HANDLERS: Dict[Type[commands.Command], Callable] = {

aws_doc_sdk_examples_tools/lliam/service_layer/run_ailly.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,50 @@
22
import logging
33
import time
44
from collections import defaultdict
5+
from dataclasses import dataclass
56
from datetime import timedelta
67
from pathlib import Path
78
from subprocess import run
89
from typing import Any, Dict, List, Optional, Set
910

1011
from aws_doc_sdk_examples_tools.lliam.domain.commands import RunAilly
12+
from aws_doc_sdk_examples_tools.lliam.domain.errors import (
13+
CommandExecutionError,
14+
DomainError,
15+
)
1116
from aws_doc_sdk_examples_tools.lliam.config import (
1217
AILLY_DIR_PATH,
1318
BATCH_PREFIX,
1419
)
1520

21+
AILLY_CMD_BASE = [
22+
"ailly",
23+
"--max-depth",
24+
"10",
25+
"--root",
26+
str(AILLY_DIR_PATH),
27+
]
28+
1629
logger = logging.getLogger(__file__)
1730

1831

1932
def handle_run_ailly(cmd: RunAilly, uow: None):
2033
resolved_batches = resolve_requested_batches(cmd.batches)
2134

35+
errors: List[DomainError] = []
36+
2237
if resolved_batches:
2338
total_start_time = time.time()
2439

2540
for batch in resolved_batches:
26-
run_ailly_single_batch(batch)
41+
try:
42+
run_ailly_single_batch(batch, cmd.packages)
43+
except FileNotFoundError as e:
44+
errors.append(
45+
CommandExecutionError(
46+
command_name=cmd.__class__.__name__, message=str(e)
47+
)
48+
)
2749

2850
total_end_time = time.time()
2951
total_duration = total_end_time - total_start_time
@@ -32,6 +54,8 @@ def handle_run_ailly(cmd: RunAilly, uow: None):
3254
f"[TIMECHECK] {num_batches} batches took {format_duration(total_duration)} to run"
3355
)
3456

57+
return errors
58+
3559

3660
def resolve_requested_batches(batch_names: List[str]) -> List[Path]:
3761
if not batch_names:
@@ -56,19 +80,26 @@ def resolve_requested_batches(batch_names: List[str]) -> List[Path]:
5680
return batch_paths
5781

5882

59-
def run_ailly_single_batch(batch: Path) -> None:
83+
def run_ailly_single_batch(batch: Path, packages: List[str] = []) -> None:
6084
"""Run ailly and process files for a single batch."""
6185
batch_start_time = time.time()
6286
iam_updates_path = AILLY_DIR_PATH / f"updates_{batch.name}.json"
6387

64-
cmd = [
65-
"ailly",
66-
"--max-depth",
67-
"10",
68-
"--root",
69-
str(AILLY_DIR_PATH),
70-
batch.name,
71-
]
88+
if packages:
89+
paths = []
90+
for package in packages:
91+
package_files = [
92+
f"{batch.name}/{p.name}" for p in batch.glob(f"*{package}*.md")
93+
]
94+
paths.extend(package_files)
95+
96+
if not paths:
97+
raise FileNotFoundError(f"No matching files found for packages: {packages}")
98+
99+
cmd = AILLY_CMD_BASE + paths
100+
else:
101+
cmd = AILLY_CMD_BASE + [batch.name]
102+
72103
logger.info(f"Running {cmd}")
73104
run(cmd)
74105

@@ -79,7 +110,9 @@ def run_ailly_single_batch(batch: Path) -> None:
79110
)
80111

81112
logger.info(f"Processing generated content for {batch.name}")
82-
process_ailly_files(input_dir=batch, output_file=iam_updates_path)
113+
process_ailly_files(
114+
input_dir=batch, output_file=iam_updates_path, packages=packages
115+
)
83116

84117

85118
EXPECTED_KEYS: Set[str] = set(["title", "title_abbrev"])
@@ -177,7 +210,10 @@ def parse_package_name(policy_update: Dict[str, str]) -> Optional[str]:
177210

178211

179212
def process_ailly_files(
180-
input_dir: Path, output_file: Path, file_pattern: str = "*.md.ailly.md"
213+
input_dir: Path,
214+
output_file: Path,
215+
file_pattern: str = "*.md.ailly.md",
216+
packages: List[str] = [],
181217
) -> None:
182218
"""
183219
Process all .md.ailly.md files in the input directory and write the results as JSON to the output file.
@@ -186,6 +222,7 @@ def process_ailly_files(
186222
input_dir: Directory containing .md.ailly.md files
187223
output_file: Path to the output JSON file
188224
file_pattern: Pattern to match files (default: "*.md.ailly.md")
225+
packages: Optional list of packages to filter by
189226
"""
190227
results = defaultdict(list)
191228

@@ -197,6 +234,13 @@ def process_ailly_files(
197234
package_name = parse_package_name(policy_update)
198235
if not package_name:
199236
raise TypeError(f"Could not get package name from policy update.")
237+
238+
if packages and package_name not in packages:
239+
logger.info(
240+
f"Skipping package {package_name} (not in requested packages)"
241+
)
242+
continue
243+
200244
results[package_name].append(policy_update)
201245

202246
with open(output_file, "w", encoding="utf-8") as out_file:

aws_doc_sdk_examples_tools/lliam/service_layer/update_doc_gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def make_title_abbreviation(old: Example, new: Example, abbreviations: Counter):
4848
version = language.versions[0]
4949
source = version.source
5050
source_title = source.title if source else ""
51-
base = f"{new.title_abbrev} (from '{source_title}' docs)"
51+
base = f"{new.title_abbrev} (from '{source_title}' guide)"
5252
abbreviations[base] += 1
5353
count = abbreviations[base]
5454
return f"{base} ({count})" if count > 1 else base

aws_doc_sdk_examples_tools/lliam/test/update_doc_gen_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ def test_update_examples_title_abbrev(doc_gen_tributary: DocGen):
4444
updated_example = doc_gen_tributary.examples["iam_policies_example"]
4545
assert (
4646
updated_example.title_abbrev
47-
== "Updated Title Abbrev (from 'AWS Account Management' docs)"
47+
== "Updated Title Abbrev (from 'AWS Account Management' guide)"
4848
)

0 commit comments

Comments
 (0)