Skip to content

Commit 79397aa

Browse files
author
Mohamed Zeidan
committed
chagned resource and operation detection
1 parent 79b4f6d commit 79397aa

File tree

4 files changed

+140
-239
lines changed

4 files changed

+140
-239
lines changed

src/sagemaker/hyperpod/cli/commands/inference.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
)
1616
from sagemaker.hyperpod.common.telemetry.constants import Feature
1717
from sagemaker.hyperpod.common.cli_decorators import handle_cli_exceptions
18+
from sagemaker.hyperpod.common.exceptions.error_constants import ResourceType, OperationType
1819

1920

2021
# CREATE
@@ -237,7 +238,10 @@ def custom_list(
237238
help="Optional. If set to `True`, the full json will be displayed",
238239
)
239240
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_js_endpoint_cli")
240-
@handle_cli_exceptions
241+
@handle_cli_exceptions(
242+
resource_type=ResourceType.HYP_JUMPSTART_ENDPOINT,
243+
operation_type=OperationType.DESCRIBE
244+
)
241245
def js_describe(
242246
name: str,
243247
namespace: Optional[str],
@@ -386,7 +390,10 @@ def js_describe(
386390
help="Optional. If set to `True`, the full json will be displayed",
387391
)
388392
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_custom_endpoint_cli")
389-
@handle_cli_exceptions
393+
@handle_cli_exceptions(
394+
resource_type=ResourceType.HYP_CUSTOM_ENDPOINT,
395+
operation_type=OperationType.DESCRIBE
396+
)
390397
def custom_describe(
391398
name: str,
392399
namespace: Optional[str],
@@ -561,7 +568,10 @@ def custom_describe(
561568
help="Optional. The namespace of the jumpstart model endpoint to delete. Default set to 'default'.",
562569
)
563570
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "delete_js_endpoint_cli")
564-
@handle_cli_exceptions
571+
@handle_cli_exceptions(
572+
resource_type=ResourceType.HYP_JUMPSTART_ENDPOINT,
573+
operation_type=OperationType.DELETE
574+
)
565575
def js_delete(
566576
name: str,
567577
namespace: Optional[str],
@@ -590,7 +600,10 @@ def js_delete(
590600
help="Optional. The namespace of the custom model endpoint to delete. Default set to 'default'.",
591601
)
592602
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "delete_custom_endpoint_cli")
593-
@handle_cli_exceptions
603+
@handle_cli_exceptions(
604+
resource_type=ResourceType.HYP_CUSTOM_ENDPOINT,
605+
operation_type=OperationType.DELETE
606+
)
594607
def custom_delete(
595608
name: str,
596609
namespace: Optional[str],
@@ -611,7 +624,10 @@ def custom_delete(
611624
help="Optional. The namespace of the jumpstart model to list pods for. Default set to 'default'.",
612625
)
613626
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "list_pods_js_endpoint_cli")
614-
@handle_cli_exceptions
627+
@handle_cli_exceptions(
628+
resource_type=ResourceType.HYP_JUMPSTART_ENDPOINT,
629+
operation_type=OperationType.LIST
630+
)
615631
def js_list_pods(
616632
namespace: Optional[str],
617633
):
@@ -632,7 +648,10 @@ def js_list_pods(
632648
help="Optional. The namespace of the custom model to list pods for. Default set to 'default'.",
633649
)
634650
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "list_pods_custom_endpoint_cli")
635-
@handle_cli_exceptions
651+
@handle_cli_exceptions(
652+
resource_type=ResourceType.HYP_CUSTOM_ENDPOINT,
653+
operation_type=OperationType.LIST
654+
)
636655
def custom_list_pods(
637656
namespace: Optional[str],
638657
):
@@ -665,7 +684,10 @@ def custom_list_pods(
665684
help="Optional. The namespace of the jumpstart model to get logs for. Default set to 'default'.",
666685
)
667686
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_logs_js_endpoint")
668-
@handle_cli_exceptions
687+
@handle_cli_exceptions(
688+
resource_type=ResourceType.HYP_JUMPSTART_ENDPOINT,
689+
operation_type=OperationType.GET
690+
)
669691
def js_get_logs(
670692
pod_name: str,
671693
container: Optional[str],
@@ -700,7 +722,10 @@ def js_get_logs(
700722
help="Optional. The namespace of the custom model to get logs for. Default set to 'default'.",
701723
)
702724
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_logs_custom_endpoint")
703-
@handle_cli_exceptions
725+
@handle_cli_exceptions(
726+
resource_type=ResourceType.HYP_CUSTOM_ENDPOINT,
727+
operation_type=OperationType.GET
728+
)
704729
def custom_get_logs(
705730
pod_name: str,
706731
container: Optional[str],

src/sagemaker/hyperpod/cli/commands/training.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
)
99
from sagemaker.hyperpod.common.telemetry.constants import Feature
1010
from sagemaker.hyperpod.common.cli_decorators import handle_cli_exceptions
11+
from sagemaker.hyperpod.common.exceptions.error_constants import ResourceType, OperationType
1112

1213

1314
@click.command("hyp-pytorch-job")
@@ -148,7 +149,10 @@ def list_jobs(namespace: str):
148149
help="Optional. The namespace of the job. Defaults to 'default' namespace.",
149150
)
150151
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_pytorchjob_cli")
151-
@handle_cli_exceptions
152+
@handle_cli_exceptions(
153+
resource_type=ResourceType.HYP_PYTORCH_JOB,
154+
operation_type=OperationType.DESCRIBE
155+
)
152156
def pytorch_describe(job_name: str, namespace: str):
153157
"""Describe a HyperPod PyTorch job."""
154158
job = HyperPodPytorchJob.get(name=job_name, namespace=namespace)
@@ -249,7 +253,10 @@ def pytorch_describe(job_name: str, namespace: str):
249253
help="Optional. The namespace of the job. Defaults to 'default' namespace.",
250254
)
251255
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "delete_pytorchjob_cli")
252-
@handle_cli_exceptions
256+
@handle_cli_exceptions(
257+
resource_type=ResourceType.HYP_PYTORCH_JOB,
258+
operation_type=OperationType.DELETE
259+
)
253260
def pytorch_delete(job_name: str, namespace: str):
254261
"""Delete a HyperPod PyTorch job."""
255262
job = HyperPodPytorchJob.get(name=job_name, namespace=namespace)
@@ -269,7 +276,10 @@ def pytorch_delete(job_name: str, namespace: str):
269276
help="Optional. The namespace of the job. Defaults to 'default' namespace.",
270277
)
271278
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "list_pods_pytorchjob_cli")
272-
@handle_cli_exceptions
279+
@handle_cli_exceptions(
280+
resource_type=ResourceType.HYP_PYTORCH_JOB,
281+
operation_type=OperationType.LIST
282+
)
273283
def pytorch_list_pods(job_name: str, namespace: str):
274284
"""List all HyperPod PyTorch pods related to the job."""
275285
job = HyperPodPytorchJob.get(name=job_name, namespace=namespace)
@@ -313,7 +323,10 @@ def pytorch_list_pods(job_name: str, namespace: str):
313323
help="Optional. The namespace of the job. Defaults to 'default' namespace.",
314324
)
315325
@_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "get_pytorchjob_logs_from_pod_cli")
316-
@handle_cli_exceptions
326+
@handle_cli_exceptions(
327+
resource_type=ResourceType.HYP_PYTORCH_JOB,
328+
operation_type=OperationType.GET
329+
)
317330
def pytorch_get_logs(job_name: str, pod_name: str, namespace: str):
318331
"""Get specific pod log for Hyperpod Pytorch job."""
319332
click.echo("Listing logs for pod: " + pod_name)

src/sagemaker/hyperpod/common/cli_decorators.py

Lines changed: 34 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -12,117 +12,49 @@
1212

1313
logger = logging.getLogger(__name__)
1414

15-
def handle_cli_exceptions(func):
15+
def handle_cli_exceptions(resource_type=None, operation_type=None):
1616
"""
17-
Smart decorator that automatically detects resource/operation types and applies
18-
enhanced 404 handling. Eliminates repetitive exception handling across CLI commands.
17+
Decorator that applies enhanced 404 handling with explicit resource/operation types.
18+
Eliminates repetitive exception handling across CLI commands.
1919
2020
This decorator:
21-
1. Auto-detects resource type from command name (hyp-jumpstart-endpoint, etc.)
22-
2. Auto-detects operation type from function name (delete, describe, etc.)
23-
3. Applies enhanced 404 handling with contextual messages
24-
4. Handles all other exceptions consistently
21+
1. Uses explicit resource_type and operation_type parameters
22+
2. Applies enhanced 404 handling with contextual messages
23+
3. Handles all other exceptions consistently
2524
2625
Usage:
27-
@handle_cli_exceptions
26+
@handle_cli_exceptions(
27+
resource_type=ResourceType.HYP_JUMPSTART_ENDPOINT,
28+
operation_type=OperationType.DELETE
29+
)
2830
@click.command("hyp-jumpstart-endpoint")
2931
def js_delete(name, namespace):
3032
# Command logic here - no try/catch needed!
3133
pass
3234
"""
33-
@functools.wraps(func)
34-
def wrapper(*args, **kwargs):
35-
try:
36-
return func(*args, **kwargs)
37-
except Exception as e:
38-
# Check if this is a 404 error that can benefit from enhanced handling
39-
if isinstance(e, ApiException) and e.status == 404:
40-
# Auto-detect resource and operation types
41-
resource_type = _detect_resource_type(func)
42-
operation_type = _detect_operation_type(func)
35+
def decorator(func):
36+
@functools.wraps(func)
37+
def wrapper(*args, **kwargs):
38+
try:
39+
return func(*args, **kwargs)
40+
except Exception as e:
41+
# Check if this is a 404 error that can benefit from enhanced handling
42+
if isinstance(e, ApiException) and e.status == 404:
43+
# Extract name and namespace from kwargs if available
44+
name = kwargs.get('name', 'unknown')
45+
namespace = kwargs.get('namespace', 'default')
46+
47+
if resource_type and operation_type:
48+
try:
49+
handle_404(name, namespace, resource_type, operation_type)
50+
except Exception as enhanced_error:
51+
click.echo(str(enhanced_error))
52+
sys.exit(1)
4353

44-
# Extract name and namespace from kwargs if available
45-
name = kwargs.get('name', 'unknown')
46-
namespace = kwargs.get('namespace', 'default')
47-
48-
if resource_type and operation_type:
49-
try:
50-
handle_404(name, namespace, resource_type, operation_type)
51-
except Exception as enhanced_error:
52-
click.echo(str(enhanced_error))
53-
sys.exit(1)
54-
55-
# For non-404 errors or when auto-detection fails, use standard handling
56-
logger.debug(f"CLI command failed: {func.__name__}", exc_info=True)
57-
click.echo(str(e))
58-
sys.exit(1)
59-
60-
return wrapper
61-
62-
63-
def _detect_resource_type(func) -> ResourceType:
64-
"""
65-
Auto-detect resource type from function name or click command name.
66-
67-
Args:
68-
func: The decorated function
69-
70-
Returns:
71-
ResourceType enum or None if not detected
72-
"""
73-
# First try to get the Click command name from the decorator
74-
try:
75-
if hasattr(func, 'name') and func.name and isinstance(func.name, str):
76-
command_name = func.name.lower()
77-
if 'jumpstart' in command_name:
78-
return ResourceType.HYP_JUMPSTART_ENDPOINT
79-
elif 'custom' in command_name:
80-
return ResourceType.HYP_CUSTOM_ENDPOINT
81-
elif 'pytorch' in command_name:
82-
return ResourceType.HYP_PYTORCH_JOB
83-
except (AttributeError, TypeError):
84-
pass
85-
86-
# Fallback to function name detection
87-
try:
88-
func_name = func.__name__.lower()
89-
90-
# Function name patterns
91-
if 'js_' in func_name or 'jumpstart' in func_name:
92-
return ResourceType.HYP_JUMPSTART_ENDPOINT
93-
elif 'custom' in func_name:
94-
return ResourceType.HYP_CUSTOM_ENDPOINT
95-
elif 'pytorch' in func_name or 'training' in func_name:
96-
return ResourceType.HYP_PYTORCH_JOB
97-
98-
except (AttributeError, TypeError):
99-
pass
100-
101-
return None
102-
103-
104-
def _detect_operation_type(func) -> OperationType:
105-
"""
106-
Auto-detect operation type from function name.
107-
108-
Args:
109-
func: The decorated function
110-
111-
Returns:
112-
OperationType enum or None if not detected
113-
"""
114-
try:
115-
func_name = func.__name__.lower()
116-
117-
if 'delete' in func_name:
118-
return OperationType.DELETE
119-
elif 'describe' in func_name or 'get' in func_name:
120-
return OperationType.DESCRIBE
121-
elif 'list' in func_name:
122-
return OperationType.LIST
123-
124-
return OperationType.GET # Default fallback
54+
# For non-404 errors or when parameters not provided, use standard handling
55+
logger.debug(f"CLI command failed: {func.__name__}", exc_info=True)
56+
click.echo(str(e))
57+
sys.exit(1)
12558

126-
except (AttributeError, TypeError, Exception):
127-
# Handle any exceptions during detection gracefully
128-
return OperationType.GET # Default fallback
59+
return wrapper
60+
return decorator

0 commit comments

Comments
 (0)