Skip to content

Commit be672f7

Browse files
ritwik-gclaude
andcommitted
Implement Task 5.2: Consistent error handling across all commands
Created centralized ErrorHandler utility with: - Consistent error format: "Error: [context] - message" - Proper stderr output for all errors - Error aggregation with bullet points - Automatic solution suggestions for common issues - Rich formatting with bracket escaping Updated all commands to use ErrorHandler: - init: File existence and creation errors - schema: Schema loading, validation, and CRUD errors - values: Value setting, validation, and management errors - validate: Schema and validation errors with proper context - generate: Generation and validation errors Error improvements: - All errors now go to stderr (not stdout) - Consistent "Error: [context] - message" format - Better error aggregation for validation failures - Actionable solution suggestions - Proper Rich formatting and bracket escaping Test updates: - Fixed all validation tests to check stderr instead of stdout - All 117 tests passing with 81% coverage - Better error message validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ce80b36 commit be672f7

File tree

10 files changed

+186
-106
lines changed

10 files changed

+186
-106
lines changed

guide/idea phase2.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- Support loading `.env` file for ENV based secrets
2+
- Support to add schemas without interaction.
3+
- value from multiple options
4+
-

helm_values_manager/commands/generate.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
import typer
66
from rich.console import Console
77

8-
from helm_values_manager.generator import GeneratorError, generate_values
8+
from helm_values_manager.errors import ErrorHandler, GeneratorError
9+
from helm_values_manager.generator import generate_values
910
from helm_values_manager.utils import get_values_file_path, load_schema, load_values
1011
from helm_values_manager.validator import validate_single_environment
1112

1213
console = Console()
13-
err_console = Console(stderr=True)
1414
app = typer.Typer()
1515

1616

@@ -24,8 +24,7 @@ def generate_command(
2424
# Load schema
2525
schema_obj = load_schema(schema)
2626
if not schema_obj:
27-
err_console.print("[red]Error:[/red] Schema file not found")
28-
raise typer.Exit(1)
27+
ErrorHandler.print_error("generate", "Schema file not found")
2928

3029
# Determine values file path
3130
values_path = values or get_values_file_path(env)
@@ -37,19 +36,14 @@ def generate_command(
3736
errors = validate_single_environment(schema_obj, values_data, env)
3837

3938
if errors:
40-
err_console.print("[red]Error:[/red] Validation failed. Please fix the following issues:")
41-
for error in errors:
42-
err_console.print(f" - {error}")
43-
raise typer.Exit(1)
39+
ErrorHandler.print_errors(errors, f"generate --env {env}")
4440

4541
# Generate values.yaml
4642
try:
4743
yaml_content = generate_values(schema_obj, values_data, env)
4844
# Output to stdout
4945
print(yaml_content, end='')
5046
except GeneratorError as e:
51-
err_console.print(f"[red]Error:[/red] {e}")
52-
raise typer.Exit(1)
47+
ErrorHandler.handle_exception(e, "generate")
5348
except Exception as e:
54-
err_console.print(f"[red]Error:[/red] Failed to generate values: {e}")
55-
raise typer.Exit(1)
49+
ErrorHandler.handle_exception(e, "generate")

helm_values_manager/commands/init.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import typer
55
from rich.console import Console
66

7+
from helm_values_manager.errors import ErrorHandler
8+
79
console = Console()
810

911
SCHEMA_FILE = "schema.json"
@@ -16,8 +18,10 @@ def init_command(
1618
schema_path = Path(SCHEMA_FILE)
1719

1820
if schema_path.exists() and not force:
19-
console.print(f"[red]Error:[/red] {SCHEMA_FILE} already exists. Use --force to overwrite.")
20-
raise typer.Exit(1)
21+
ErrorHandler.print_error(
22+
"init",
23+
f"{SCHEMA_FILE} already exists. Use --force to overwrite."
24+
)
2125

2226
initial_schema = {
2327
"values": []
@@ -29,5 +33,4 @@ def init_command(
2933

3034
console.print(f"[green]✓[/green] Created {SCHEMA_FILE}")
3135
except Exception as e:
32-
console.print(f"[red]Error:[/red] Failed to create schema file: {e}")
33-
raise typer.Exit(1)
36+
ErrorHandler.handle_exception(e, "init")

helm_values_manager/commands/schema.py

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from rich.console import Console
66
from rich.prompt import Confirm, Prompt
77

8+
from helm_values_manager.errors import ErrorHandler, SchemaError, error_console
89
from helm_values_manager.models import SchemaValue, ValueType
910
from helm_values_manager.utils import load_schema, save_schema, validate_key_unique, validate_path_format
1011

@@ -22,15 +23,15 @@ def parse_value_by_type(value_str: str, value_type: ValueType) -> Any:
2223
return float(value_str)
2324
return int(value_str)
2425
except ValueError:
25-
raise typer.BadParameter(f"Invalid number: {value_str}")
26+
raise SchemaError(f"Invalid number: {value_str}")
2627
elif value_type == "boolean":
2728
lower = value_str.lower()
2829
if lower in ("true", "yes", "y", "1"):
2930
return True
3031
elif lower in ("false", "no", "n", "0"):
3132
return False
3233
else:
33-
raise typer.BadParameter(f"Invalid boolean: {value_str}")
34+
raise SchemaError(f"Invalid boolean: {value_str}")
3435
elif value_type == "array":
3536
try:
3637
return json.loads(value_str)
@@ -41,7 +42,7 @@ def parse_value_by_type(value_str: str, value_type: ValueType) -> Any:
4142
try:
4243
return json.loads(value_str)
4344
except json.JSONDecodeError:
44-
raise typer.BadParameter(f"Invalid JSON object: {value_str}")
45+
raise SchemaError(f"Invalid JSON object: {value_str}")
4546

4647

4748
@app.command("add")
@@ -52,20 +53,19 @@ def add_command(
5253
# Load existing schema
5354
schema = load_schema(schema_path)
5455
if not schema:
55-
console.print("[red]Error:[/red] No schema.json found. Run 'init' first.")
56-
raise typer.Exit(1)
56+
ErrorHandler.print_error("schema", "No schema.json found. Run 'init' first.")
5757

5858
console.print("[bold]Add new value to schema[/bold]\n")
5959

6060
# Prompt for key
6161
while True:
6262
key = Prompt.ask("Key (unique identifier)")
6363
if not key:
64-
console.print("[red]Key cannot be empty[/red]")
64+
error_console.print("[red]Key cannot be empty[/red]")
6565
continue
6666

6767
if not validate_key_unique(schema, key):
68-
console.print(f"[red]Key '{key}' already exists in schema[/red]")
68+
error_console.print(f"[red]Key '{key}' already exists in schema[/red]")
6969
continue
7070

7171
break
@@ -74,7 +74,7 @@ def add_command(
7474
while True:
7575
path = Prompt.ask("Path (dot-separated YAML path)")
7676
if not validate_path_format(path):
77-
console.print("[red]Invalid path format. Use alphanumeric characters and dots only.[/red]")
77+
error_console.print("[red]Invalid path format. Use alphanumeric characters and dots only.[/red]")
7878
continue
7979
break
8080

@@ -88,7 +88,7 @@ def add_command(
8888
value_type = Prompt.ask("Type", default="string").lower()
8989
if value_type in type_choices:
9090
break
91-
console.print(f"[red]Invalid type. Choose from: {', '.join(type_choices)}[/red]")
91+
error_console.print(f"[red]Invalid type. Choose from: {', '.join(type_choices)}[/red]")
9292

9393
# Prompt for required
9494
required = Confirm.ask("Required?", default=True)
@@ -101,8 +101,8 @@ def add_command(
101101
try:
102102
default = parse_value_by_type(default_str, value_type)
103103
break
104-
except typer.BadParameter as e:
105-
console.print(f"[red]{e}[/red]")
104+
except SchemaError as e:
105+
error_console.print(f"[red]{e}[/red]")
106106

107107
# Prompt for sensitive
108108
sensitive = Confirm.ask("Sensitive value?", default=False)
@@ -139,8 +139,7 @@ def list_command(
139139
"""List all values in the schema."""
140140
schema = load_schema(schema_path)
141141
if not schema:
142-
console.print("[red]Error:[/red] No schema.json found. Run 'init' first.")
143-
raise typer.Exit(1)
142+
ErrorHandler.print_error("schema", "No schema.json found. Run 'init' first.")
144143

145144
if not schema.values:
146145
console.print("No values defined in schema.")
@@ -168,14 +167,12 @@ def get_command(
168167
"""Show details of a specific schema value."""
169168
schema = load_schema(schema_path)
170169
if not schema:
171-
console.print("[red]Error:[/red] No schema.json found. Run 'init' first.")
172-
raise typer.Exit(1)
170+
ErrorHandler.print_error("schema", "No schema.json found. Run 'init' first.")
173171

174172
# Find the value
175173
value = next((v for v in schema.values if v.key == key), None)
176174
if not value:
177-
console.print(f"[red]Error:[/red] Value with key '{key}' not found")
178-
raise typer.Exit(1)
175+
ErrorHandler.print_error("schema", f"Value with key '{key}' not found")
179176

180177
# Display details
181178
console.print(f"\n[bold]{value.key}[/bold]")
@@ -196,14 +193,12 @@ def update_command(
196193
"""Update an existing schema value."""
197194
schema = load_schema(schema_path)
198195
if not schema:
199-
console.print("[red]Error:[/red] No schema.json found. Run 'init' first.")
200-
raise typer.Exit(1)
196+
ErrorHandler.print_error("schema", "No schema.json found. Run 'init' first.")
201197

202198
# Find the value
203199
value_index = next((i for i, v in enumerate(schema.values) if v.key == key), None)
204200
if value_index is None:
205-
console.print(f"[red]Error:[/red] Value with key '{key}' not found")
206-
raise typer.Exit(1)
201+
ErrorHandler.print_error("schema", f"Value with key '{key}' not found")
207202

208203
value = schema.values[value_index]
209204
console.print(f"[bold]Updating '{key}'[/bold]\n")
@@ -213,7 +208,7 @@ def update_command(
213208
new_path = Prompt.ask(f"Path [{value.path}]", default=value.path)
214209
if new_path != value.path:
215210
while not validate_path_format(new_path):
216-
console.print("[red]Invalid path format. Use alphanumeric characters and dots only.[/red]")
211+
error_console.print("[red]Invalid path format. Use alphanumeric characters and dots only.[/red]")
217212
new_path = Prompt.ask(f"Path [{value.path}]", default=value.path)
218213
value.path = new_path
219214

@@ -227,7 +222,7 @@ def update_command(
227222
new_type = Prompt.ask(f"Type [{value.type}]", default=value.type).lower()
228223
if new_type != value.type:
229224
while new_type not in type_choices:
230-
console.print(f"[red]Invalid type. Choose from: {', '.join(type_choices)}[/red]")
225+
error_console.print(f"[red]Invalid type. Choose from: {', '.join(type_choices)}[/red]")
231226
new_type = Prompt.ask(f"Type [{value.type}]", default=value.type).lower()
232227
value.type = new_type
233228
# Clear default if type changed
@@ -246,8 +241,8 @@ def update_command(
246241
try:
247242
value.default = parse_value_by_type(default_str, value.type)
248243
break
249-
except typer.BadParameter as e:
250-
console.print(f"[red]{e}[/red]")
244+
except SchemaError as e:
245+
error_console.print(f"[red]{e}[/red]")
251246
else:
252247
default_display = json.dumps(value.default) if value.type in ['array', 'object'] else str(value.default)
253248
console.print(f"\nCurrent default value: {default_display}")
@@ -271,13 +266,13 @@ def update_command(
271266
try:
272267
value.default = parse_value_by_type(default_str, value.type)
273268
break
274-
except typer.BadParameter as e:
275-
console.print(f"[red]{e}[/red]")
269+
except SchemaError as e:
270+
error_console.print(f"[red]{e}[/red]")
276271
break
277272
elif choice == "3":
278273
# Remove default value
279274
if value.required:
280-
console.print("[yellow]Warning:[/yellow] This field is required but will have no default value")
275+
ErrorHandler.print_warning("This field is required but will have no default value")
281276
if not Confirm.ask("Continue removing default?", default=False):
282277
continue
283278

@@ -303,14 +298,12 @@ def remove_command(
303298
"""Remove a value from the schema."""
304299
schema = load_schema(schema_path)
305300
if not schema:
306-
console.print("[red]Error:[/red] No schema.json found. Run 'init' first.")
307-
raise typer.Exit(1)
301+
ErrorHandler.print_error("schema", "No schema.json found. Run 'init' first.")
308302

309303
# Find the value
310304
value_index = next((i for i, v in enumerate(schema.values) if v.key == key), None)
311305
if value_index is None:
312-
console.print(f"[red]Error:[/red] Value with key '{key}' not found")
313-
raise typer.Exit(1)
306+
ErrorHandler.print_error("schema", f"Value with key '{key}' not found")
314307

315308
value = schema.values[value_index]
316309

helm_values_manager/commands/validate.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import typer
77
from rich.console import Console
88

9+
from helm_values_manager.errors import ErrorHandler
910
from helm_values_manager.models import Schema
1011
from helm_values_manager.utils import load_schema, load_values, get_values_file_path
1112
from helm_values_manager.validator import validate_single_environment
@@ -31,20 +32,17 @@ def validate_command(
3132
# Check if schema file exists first
3233
schema_path = Path(schema)
3334
if not schema_path.exists():
34-
console.print("[red]Error:[/red] Schema file not found")
35-
raise typer.Exit(1)
35+
ErrorHandler.print_error("validate", "Schema file not found")
3636

3737
# Load schema
3838
try:
3939
with open(schema_path) as f:
4040
data = json.load(f)
4141
schema_obj = Schema(**data)
4242
except json.JSONDecodeError:
43-
console.print("[red]Error:[/red] Invalid JSON in schema file")
44-
raise typer.Exit(1)
43+
ErrorHandler.print_error("validate", "Invalid JSON in schema file")
4544
except Exception as e:
46-
console.print(f"[red]Error:[/red] Invalid schema: {e}")
47-
raise typer.Exit(1)
45+
ErrorHandler.print_error("validate", f"Invalid schema: {e}")
4846

4947
# Load values
5048
values_data = load_values(env, values)
@@ -53,11 +51,6 @@ def validate_command(
5351
errors = validate_single_environment(schema_obj, values_data, env)
5452

5553
if errors:
56-
console.print("[red]Error:[/red] Validation failed:")
57-
for error in errors:
58-
# Escape square brackets for Rich
59-
escaped_error = error.replace("[", "\\[").replace("]", "]")
60-
console.print(f" - {escaped_error}")
61-
raise typer.Exit(1)
54+
ErrorHandler.print_errors(errors, f"validate --env {env}")
6255
else:
6356
console.print(f"[green]✅[/green] Validation passed for environment: {env}")

0 commit comments

Comments
 (0)