Error Handling Guide
This document provides an in-depth overview of the error handling mechanisms within the project. It details the exceptions used, their meanings, associated exit codes, and examples of when and how they are raised in the codebase. The guide aims to standardize error handling practices and ensure consistency across the project, aligning with CEP-8 standards.
Exceptions Overview
Multicast Exceptions
CommandExecutionError
Exception:
multicast.exceptions.CommandExecutionErrorDescription: Raised when a command execution fails, capturing the error message and exit code.
Exit Code: Defined at the time of raising, typically
1(General Error).Usage Example:
This exception is typically raised when a subprocess or system command fails to execute properly.
if return_code != 0: raise CommandExecutionError("Failed to execute the backup script.", exit_code=return_code)
ShutdownCommandReceived
Exception:
multicast.exceptions.ShutdownCommandReceivedDescription: Raised to signal a graceful shutdown of the multicast server.
Exit Code:
143(Terminated by SIGTERM)Usage Example:
Raised when a shutdown command is received, allowing the server to terminate gracefully.
if command == "SHUTDOWN": raise ShutdownCommandReceived("Shutdown command received.") from None
Standard Exceptions Mapped to Exit Codes
The codebase maps several standard exceptions to specific exit codes according to the
project’s EXIT_CODES mapping.
ModuleNotFoundError
Description: Raised when a required module cannot be found.
Exit Code:
127(Command Not Found)Usage Examples:
Example 1: In
multicast/__init__.py, when critical modules fail to import.try: import important_module except ImportError as _cause: raise ModuleNotFoundError( "[CWE-758] Module failed to import." ) from _causeExample 2: In
multicast/hear.py, handling errors during module import.raise ModuleNotFoundError( "[CWE-758] Module failed completely." ) from None
NotImplementedError
Description: Raised when a method or function is declared but not implemented.
Exit Code:
70(Internal Software Error)Usage Example:
def some_abstract_method(self): raise NotImplementedError("Subclasses must implement this method.")
ImportError
Description: Raised when an import statement fails to find the module definition or when a
from ... importfails to find a name that is to be imported.Exit Code:
70(Internal Software Error)Usage Example:
In
multicast/__main__.py, wrapping the originalImportErrorto provide additional context.except ImportError as _cause: raise ImportError("[CWE-440] Error Importing Python") from _cause
OSError
Description: Raised when a system function returns a system-related error.
Exit Code:
2(Misuse of Shell Builtins)Usage Example:
In
multicast/recv.py, when a socket operation fails.except OSError as _cause: raise OSError("[CWE-440] Socket operation failed.") from _cause
FileNotFoundError
Description: Raised when a required file is not found.
Exit Code: 66 (No Input)
Usage Example:
raise FileNotFoundError("Configuration file not found.")
ValueError
Description: Raised when a function receives an argument of the correct type but an inappropriate value.
Exit Code: 65 (Data Error)
Usage:
raise ValueError("Invalid value provided for parameter 'x'.")
PermissionError
Description: Raised when attempting an operation without adequate access rights.
Exit Code: 77 (Permission Denied)
Usage:
raise PermissionError("Insufficient permissions to access the resource.")
Exit Codes Mapping (EXIT_CODES)
The EXIT_CODES dictionary in multicast/exceptions.py provides a centralized mapping between
exit codes, exceptions, and messages, adhering to
CEP-8.
For clarity, here’s the standard mapping:
Exception |
Exit Code |
Message |
|---|---|---|
|
1 |
General Error |
|
2 |
Misuse of Shell Builtins |
|
64 |
Usage Error |
|
65 |
Data Error |
|
66 |
No Input |
|
69 |
Unavailable Service |
|
70 |
Internal Software Error |
|
77 |
Permission Denied |
|
125 |
Critical Failure |
|
126 |
Command Invoked Cannot Execute |
|
127 |
Command Not Found |
|
130 |
Interrupt (SIGINT) |
|
141 |
Broken Pipe (SIGPIPE) |
|
143 |
Terminated (SIGTERM) |
Implementation
The EXIT_CODES dictionary in multicast/exceptions.py defines the mapping between exit codes,
exceptions, and their descriptions. This centralized mapping ensures consistency across the
project.
EXIT_CODES = {
0: (None, 'Success'),
1: (RuntimeError, 'General Error'),
2: (OSError, 'Misuse of Shell Builtins'),
64: (argparse.ArgumentError, 'Usage Error'),
65: (ValueError, 'Data Error'),
66: (FileNotFoundError, 'No Input'),
69: (ConnectionError, 'Unavailable Service'),
70: (Exception, 'Internal Software Error'),
77: (PermissionError, 'Permission Denied'),
125: (BaseException, 'Critical Failure'),
126: (AssertionError, 'Command Invoked Cannot Execute'),
127: (ModuleNotFoundError, 'Command Not Found'),
129: (None, 'Hangup (SIGHUP)'),
130: (KeyboardInterrupt, 'Interrupt (SIGINT)'),
134: (None, 'Abort (SIGABRT)'),
137: (None, 'Killed (SIGKILL)'),
141: (BrokenPipeError, 'Broken Pipe (SIGPIPE)'),
143: (SystemExit, 'Terminated (SIGTERM)'),
255: (None, 'Exit Status Out of Range'),
}
[!TIP] Plan Ahead: Custom exceptions like CommandExecutionError can use exit codes in the range
1-255, but should avoid conflicting with the standard mappings defined above by CEP-8.
[!NOTE] In some situations, it may not be appropriate to pass the original exception back to the caller. In these cases, the handler within the
multicastmodule may add additional context by raising a custom multicast exception, chaining it to the underlying exception. This approach enriches the error information while maintaining clarity in the exception hierarchy.
The following mappings are specific to multicast exceptions and may differ from the standard mappings above (while still remaining CEP-8 compliant):
Exception |
Exit Code |
Reason for Customization |
|---|---|---|
|
1-255 |
Multicast Runtime Error |
|
143 |
Terminated by “SHUTDOWN” |
Exception Usage Examples
Below are examples of how exceptions are raised within the codebase, illustrating actual usage in different modules. These examples are directly taken from the source code to provide real-world context.
Example 1: ModuleNotFoundError in multicast/__init__.py
Location:
multicast/__init__.py, Lines 332-356When the program fails to import critical modules, such as
argparse,unicodedata,socket, orstruct, it raises aModuleNotFoundError.try: import argparse except ImportError as _casue: raise ModuleNotFoundError("[CWE-440] We could not import argparse. ABORT.") from _cause
Example 2: NotImplementedError in multicast/__init__.py
Location:
multicast/__init__.py, Line 616Abstract methods that must be implemented by subclasses.
def run(self): raise NotImplementedError("[CWE-758] Subclasses must implement this method.")
Example 3: ImportError in multicast/__main__.py
Location:
multicast/__main__.py, Line 86Handling import errors with additional context.
except ImportError as _cause: raise ImportError("[CWE-440] Error Importing Python") from _cause
Example 4: ShutdownCommandReceived in multicast/hear.py
Location:
multicast/hear.py, Line 449Signaling a graceful shutdown when a shutdown command is received.
if message == "SHUTDOWN": raise ShutdownCommandReceived("SHUTDOWN") from None
Example 5: OSError in multicast/recv.py
Location:
multicast/recv.py, Line 292Indicating a socket operation failure.
except OSError as _cause: raise OSError("[CWE-474] Socket operation failed.") from _cause
Example 6: KeyboardInterrupt in multicast/hear.py
Location:
multicast/hear.py, Line 541Handling user interruption (e.g., pressing Ctrl+C).
except KeyboardInterrupt as _cause: raise KeyboardInterrupt("User interrupted the process.") from _cause
Error Handling Practices
Centralized Exception Handling: Utilize the
exit_on_exceptiondecorator provided inmulticast/exceptions.pyto ensure exceptions are mapped to the correct exit codes and handled consistently.Consistent Use of Exceptions: Keep exception raising consistent across the codebase by using the predefined exceptions and messages.
Clear Error Messages: Provide informative and clear error messages to aid in debugging and user understanding.
Adherence to Standards: Follow the guidelines set out in CEP-8 and the project’s conventions for error handling and exit codes.
Adding New Exceptions
When introducing new exceptions:
Define the Exception: Create the new exception class, inheriting from an appropriate base class.
Update
EXIT_CODESMapping: Add the exception and its corresponding exit code to theEXIT_CODESdictionary inmulticast/exceptions.py.Document the Exception: Update this guide to include the new exception, its description, exit code, and usage examples.
Implement Tests: Write tests to cover the new exception and ensure it’s handled correctly by the error handling system.
Conclusion
This guide serves as a comprehensive reference for the project’s error handling mechanisms. By following these practices, developers can maintain consistency, improve code quality, and provide a better experience for users interacting with the software.