Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-mlc-core-actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ jobs:

- name: Test for rm cache - invalid cache entry tags(test succeeds if command fails)
run: |
! mlc rm cache --tags=sample,invalid,tags
mlc rm cache --tags=sample,invalid,tags

- name: Test for rm cache when the cache folder is empty(only for mlc rm cache without specifying particular script)
run: |
Expand Down
85 changes: 85 additions & 0 deletions docs/error_codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Error and Warning Codes in MLCFlow

MLCFlow uses a standardized system for error and warning codes to provide consistent error handling across the framework. This document explains the error code system and how to use it.

## Overview

The error code system consists of two main components:

1. **WarningCode(1000-1007)**: Enum class for warning codes (return = 0, with warning_code field)
2. **ErrorCode(2000-2007)**: Enum class for error codes (return > 0)
<!--
## Error Code Structure

Error codes are organized by category:

- **General errors (1000-1099)**: Common errors that can occur in any part of the system
- **Script errors (1100-1199)**: Errors specific to script execution and management
- **Repository errors (1200-1299)**: Errors related to repository operations
- **Cache errors (1300-1399)**: Errors related to cache operations

## Warning Code Structure

Warning codes follow a similar structure:

- **General warnings (2000-99)**: Common warnings that can occur in any part of the system
- **Script warnings (2100-2199)**: Warnings specific to script execution and management
- **Repository warnings (2200-2299)**: Warnings related to repository operations
- **Cache warnings (2300-2399)**: Warnings related to cache operations -->

## Usage

### In MLCFlow Framework

When returning an error:

```python
from mlc.error_codes import ErrorCode, get_error_message

return {'return': ErrorCode.UNSUPPORTED_OS.code, 'error': get_error_message(ErrorCode.UNSUPPORTED_OS.description)}
```

When returning a warning:

```python
from mlc.error_codes import WarningCode, get_warning_message

return {'return': 0, 'warning_code': WarningCode.ELEVATED_PERMISSION_NEEDED.code, 'warning': get_warning_message(WarningCode.ELEVATED_PERMISSION_NEEDED.description)}
```

### In Scripts

When checking for errors or warnings:

```python
from mlc.error_codes import is_warning_code

result = mlc_cache.access({'action': 'rm', 'target': 'cache', 'tags': cache_rm_tags, 'f': True})
if result['return'] != 0 and not is_warning_code(result['return']):
# Handle error
return result
```

## Helper Functions

The error code system provides several helper functions:

- `get_error_message(error_code)`: Get the description for an error code
- `get_warning_message(warning_code)`: Get the description for a warning code
- `is_warning_code(code)`: Check if a code is a warning code
- `is_error_code(code)`: Check if a code is an error code
- `get_code_type(code)`: Get the type of a code (error, warning, or unknown)

## Adding New Error or Warning Codes

To add a new error or warning code, update the appropriate enum class in `mlc/error_codes.py`:

```python
# For a new error code
NEW_ERROR = (2008, "Description of the new error")

# For a new warning code
NEW_WARNING = (1007, "Description of the new warning")
```

Make sure to follow the category structure and use the next available code in the appropriate range.
6 changes: 4 additions & 2 deletions mlc/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .index import Index
from .repo import Repo
from .item import Item
from .error_codes import WarningCode

# Base class for actions
class Action:
Expand Down Expand Up @@ -340,9 +341,10 @@ def rm(self, i):
# Do not error out if fetch_all is used
if inp.get("fetch_all", False) == True:
logger.warning(f"{target_name} is empty! nothing to be cleared!")
return {"return": 0}
return {"return": 0, "warnings": [{"code": WarningCode.EMPTY_TARGET.code, "description": f"{target_name} is empty! nothing to be cleared!"}]}
else:
return {'return': 16, 'error': f'No {target_name} found for {inp}'}
logger.warning(f"No {target_name} found for {inp}")
return {'return': 0, "warnings": [{"code": WarningCode.EMPTY_TARGET.code, "description": f"No {target_name} found for {inp}"}]}
elif len(res['list']) > 1:
logger.info(f"More than 1 {target_name} found for {inp}:")
if not i.get('all'):
Expand Down
77 changes: 77 additions & 0 deletions mlc/error_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from enum import Enum, auto

class ErrorCode(Enum):
"""Enum class for error codes in MLCFlow"""
# General errors (2000-2007)
AUTOMATION_SCRIPT_NOT_FOUND = (2000, "The specified automation script was not found")
PATH_DOES_NOT_EXIST = (2001, "Provided path does not exists")
FILE_NOT_FOUND = (2002, "Required file was not found")
PERMISSION_DENIED = (2003, "Insufficient permission to execute the script")
IO_Error = (2004, "File I/O operation failed")
AUTOMATION_CUSTOM_ERROR = (2005, "Custom error triggered by the script")
UNSUPPORTED_OS = (2006, "The Operating System is not supported by the script")
MISSING_ENV_VARIABLE = (2007, "Required environment variables are missing")

def __init__(self, code, description):
self.code = code
self.description = description

class WarningCode(Enum):
"""Enum class for warning codes in MLCFlow"""
# General warnings (1000-1007)
IO_WARNING = (1000, "File I/O operation warning")
AUTOMATION_SCRIPT_NOT_TESTED = (1001, "the script is not tested on the current operatinig system or is in a development state")
AUTOMATION_SCRIPT_SKIPPED = (1002, "The script has been skipped during execution")
AUTOMATION_CUSTOM_ERROR = (1003, "Custom warning triggered by the script")
NON_INTERACTIVE_ENV = (1004, "Non interactive environment detected")
ELEVATED_PERMISSION_NEEDED = (1005, "Elevated permission needed")
EMPTY_TARGET = (1006, "The specified target is empty")

def __init__(self, code, description):
self.code = code
self.description = description

def get_error_info(error_code):
"""Get the error message for a given error code"""
try:
return {"error_code": ErrorCode(error_code).code, "error_message": ErrorCode(error_code).description}
except ValueError:
return f"Unknown error code: {error_code}"

def get_warning_info(warning_code):
"""Get the warning message for a given warning code"""
try:
return {"warning_code": WarningCode(warning_code).code, "warning_message": WarningCode(warning_code).description}
except ValueError:
return f"Unknown warning code: {warning_code}"

def is_warning_code(code):
"""Check if a given code is a warning code"""
try:
# Check if code is in warning range (2000-2399)
if 2000 <= code <= 2399:
WarningCode(code)
return True
return False
except ValueError:
return False

def is_error_code(code):
"""Check if a given code is an error code"""
try:
# Check if code is in error range (1000-1399)
if 1000 <= code <= 1399:
ErrorCode(code)
return True
return False
except ValueError:
return False

def get_code_type(code):
"""Get the type of a code (error or warning)"""
if is_error_code(code):
return "error"
elif is_warning_code(code):
return "warning"
else:
return "unknown"
4 changes: 4 additions & 0 deletions mlc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def process_console_output(res, target, action, run_args):
else:
for item in res['list']:
logger.info(f"""Item path: {item.path}""")
if "warnings" in res:
logger.warning(f"{len(res['warnings'])} warning(s) found during the execution of the mlc command.")
for warning in res["warnings"]:
logger.warning(f"Warning code: {warning['code']}, Discription: {warning['description']}")

if default_parent is None:
default_parent = Action()
Expand Down
1 change: 0 additions & 1 deletion mlc/script_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ def call_script_module_function(self, function_name, run_args):
module_path = os.path.join(script_path, "module.py")
module = self.dynamic_import_module(module_path)


# Check if ScriptAutomation is defined in the module
if hasattr(module, 'ScriptAutomation'):
automation_instance = module.ScriptAutomation(self, module_path)
Expand Down
Loading