-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
**Refactor the exit code handling.** #117
Comments
@coderabbitai, please read all the source files in the project and extract all numerical return values and exit codes from both shell-scripts and python code. Additionally read CEP-8 focusing on it's impacts to exit codes. Then provide a detailed summary of the findings including an enumeration of the different exit codes. |
Tip For best results, initiate chat on the files or code changes. I've analyzed the source files in the project and extracted the numerical return values and exit codes from both shell scripts and Python code. Additionally, I've reviewed CEP-8, focusing on its impacts on exit codes. Summary of Findings: 1. Exit Codes in Shell ScriptsThe following exit codes are used in the shell scripts:
2. Exit Codes in Python Scripts
3. Compliance with CEP-8 and POSIX StandardsCEP-8 emphasizes adherence to POSIX standard exit codes:
Recommendations:
Conclusion: The project's current use of exit codes largely aligns with the POSIX standards as outlined in CEP-8. By reviewing and documenting the custom exit codes, we can enhance clarity, maintainability, and portability of the scripts across different Unix-like systems. If you have any questions or need further assistance, please let me know. 🔗 Analysis chain🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Extract all numerical exit codes from shell scripts
rg --type sh 'exit [0-9]+' -n -H -A0
# Extract all numerical exit codes from Python scripts
rg --type py 'sys\.exit\([0-9]+\)' -n -H -A0
# Fetch CEP-8 content for analysis
curl -s https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161
Length of output: 121062 Script: #!/bin/bash
# Extract all numerical exit codes from shell scripts
rg 'exit [0-9]+' -n -H
# Extract all numerical exit codes from Python scripts
rg 'sys\.exit\([0-9]+\)' -n -H
# Fetch CEP-8 raw content for analysis
curl -s https://gist.githubusercontent.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161/raw/CEP-8-DRAFT.md
Length of output: 17421 🧠 Learnings used
|
💁 Supplemental findings:
|
🤔 1. For the trivial failures:
|
@coderabbitai, please carefully read:
After reading up, please draft a CEP document, titled: "Exit Codes and Meanings" |
Tip For best results, initiate chat on the files or code changes. I've drafted the CEP document titled "Exit Codes and Meanings" as per your request. Please find the draft below. CEP-XX: Exit Codes and MeaningsTitle: Standardization of Exit Codes in Scripts and Programs Author(s): [Your Name] Status: Draft Created: October 2024 AbstractThis CEP proposes the standardization of exit codes used across all shell scripts and Python programs within the project. By aligning with POSIX standards and best practices, we aim to enhance consistency, readability, and maintainability of our codebase. This standardization will facilitate easier debugging and contribute to a more predictable execution flow. MotivationThe current use of exit codes in the project is varied, with some scripts employing custom codes that may not be immediately understood by contributors or users. This lack of standardization can lead to confusion and makes debugging more challenging. Standardizing exit codes will:
Detailed ExplanationStandard Exit CodesWe propose adopting the following standard exit codes as per POSIX and commonly accepted conventions:
Custom Exit CodesFor specific conditions unique to our project, we may define custom exit codes within the range 64–113, as recommended to avoid conflicts with standard codes:
Guidelines for Implementation
Impact on Existing Codebase
Reference Materials
ConclusionAdopting standardized exit codes across our project will greatly enhance the maintainability and reliability of our scripts. This CEP serves as a guideline for all contributors to follow, ensuring that our codebase adheres to best practices and is accessible to both new and existing team members. Action Items
Please review the draft and let me know if any changes or additions are needed. ✏️ Learnings added
🧠 Learnings used
|
|
Tip For best results, initiate chat on the files or code changes. I've updated CEP-8 as per your instructions. I have integrated the content from "Exit Codes and Meanings" into the existing CEP-8 draft and changed the custom exit code Please find the updated CEP-8 document below: CEP-8: Standardization of Exit Codes in Scripts and ProgramsTitle: Standardization of Exit Codes in Scripts and Programs Author(s): [Your Name] Status: Draft Created: October 2024 AbstractThis CEP proposes the standardization of exit codes used across all shell scripts and Python programs within the project. By aligning with POSIX standards and best practices, we aim to enhance consistency, readability, and maintainability of our codebase. This standardization will facilitate easier debugging and contribute to a more predictable execution flow. MotivationThe current use of exit codes in the project is varied, with some scripts employing custom codes that may not be immediately understood by contributors or users. This lack of standardization can lead to confusion and makes debugging more challenging. Standardizing exit codes will:
Detailed ExplanationStandard Exit CodesWe propose adopting the following standard exit codes as per POSIX and commonly accepted conventions:
Custom Exit CodesTo avoid conflicts with standard codes, custom exit codes should be minimized. When necessary, they should be within the range 64–113 as per recommendations, but standard codes are preferred. Update: We are replacing the custom exit code Guidelines for Implementation
Impact on Existing Codebase
Reference Materials
ConclusionAdopting standardized exit codes across our project will greatly enhance the maintainability and reliability of our scripts. This CEP serves as a guideline for all contributors to follow, ensuring that our codebase adheres to best practices and is accessible to both new and existing team members. Action Items
Please review the updated CEP-8 draft and let me know if any further changes are needed. ✏️ Learnings added
🧠 Learnings used
|
@coderabbitai, I have pushed an update with the exit code revisions to CEP-8.
|
Tip For best results, initiate chat on the files or code changes. I've reviewed the output of the scripts and the updated CEP-8 document. Python Dictionary of Exit Codes and Possible Messages Based on the exit codes defined in CEP-8, here's the Python dictionary: EXIT_CODES = {
0: 'Success',
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', # Replacing custom code 89 with 125
126: 'Command Invoked Cannot Execute',
127: 'Command Not Found',
# Signal-related exit codes
128 + 1: 'Hangup (SIGHUP)',
128 + 2: 'Interrupt (SIGINT)',
128 + 3: 'Quit (SIGQUIT)',
128 + 6: 'Abort (SIGABRT)',
128 + 9: 'Killed (SIGKILL)',
128 + 13: 'Broken Pipe (SIGPIPE)',
128 + 15: 'Terminated (SIGTERM)',
255: 'Exit Status Out of Range',
} This dictionary can be placed in a common module (e.g., Identifying Opportunities to Refactor Exit Codes The script output indicates that there are no To align with CEP-8 and standardize exit codes:
Example Refactoring Here's an example of how you might refactor a Python script: import sys
EXIT_CODES = {
0: 'Success',
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',
128 + 1: 'Hangup (SIGHUP)',
128 + 2: 'Interrupt (SIGINT)',
128 + 3: 'Quit (SIGQUIT)',
128 + 6: 'Abort (SIGABRT)',
128 + 9: 'Killed (SIGKILL)',
128 + 13: 'Broken Pipe (SIGPIPE)',
128 + 15: 'Terminated (SIGTERM)',
255: 'Exit Status Out of Range',
}
def main():
try:
# ... main code ...
# Example of missing input file
if not input_file_exists:
print("Error: Input file not found.")
sys.exit(66) # No Input
# Example of unavailable service
if not service_available:
print("Error: Required service is unavailable.")
sys.exit(69) # Unavailable Service
# ... rest of the code ...
except PermissionError as e:
print(f"Error: {e}")
sys.exit(77) # Permission Denied
except KeyboardInterrupt:
print("Process interrupted by user.")
sys.exit(130) # Interrupt (SIGINT)
except Exception as e:
print(f"Unexpected Error: {e}")
sys.exit(70) # Internal Software Error
if __name__ == '__main__':
main() Next Steps
Please let me know if you need further assistance or have any questions. 🔗 Analysis chainTo assist in generating the Python dictionary of exit codes and possible messages, and to identify opportunities to refactor exit codes with the new dictionary in the 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Extract all sys.exit() calls from Python files in the 'multicast' directory
rg --type py 'sys\.exit\([0-9]+\)' -n -H multicast/
# Fetch the updated CEP-8 document for reference
curl -s https://gist.githubusercontent.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161/raw/CEP-8.md
Length of output: 9026 ✏️ Learnings added
🧠 Learnings used
|
@coderabbitai, wouldn't refactoring look more like this: import sys
EXIT_CODES = {
0: 'Success',
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',
128 + 1: 'Hangup (SIGHUP)',
128 + 2: 'Interrupt (SIGINT)',
128 + 3: 'Quit (SIGQUIT)',
128 + 6: 'Abort (SIGABRT)',
128 + 9: 'Killed (SIGKILL)',
128 + 13: 'Broken Pipe (SIGPIPE)',
128 + 15: 'Terminated (SIGTERM)',
255: 'Exit Status Out of Range',
}
def main():
try:
# ... main code ...
# Example of missing input file
if not input_file_exists:
print(EXIT_CODES[66])
sys.exit(66) # No Input
# Example of unavailable service
if not service_available:
print(EXIT_CODES[69])
sys.exit(69) # Unavailable Service
# ... rest of the code ...
except PermissionError as e:
print(f"{EXIT_CODES[77]}: {e}")
sys.exit(77) # Permission Denied
except KeyboardInterrupt:
print(EXIT_CODES[130])
sys.exit(130) # Interrupt (SIGINT)
except Exception as e:
print(f"{EXIT_CODES[70]}: {e}")
sys.exit(70) # Internal Software Error
except BaseException as e:
print(f"{EXIT_CODES[125]}: {e}")
sys.exit(125) # Critical Software Error
if __name__ == '__main__':
main() |
Tip For best results, initiate chat on the files or code changes. You're absolutely right! Your refactored code is more efficient and aligns better with centralizing both the exit codes and their corresponding messages. By utilizing the Here's the updated refactored code: import sys
EXIT_CODES = {
0: 'Success',
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',
129: 'Hangup (SIGHUP)',
130: 'Interrupt (SIGINT)',
131: 'Quit (SIGQUIT)',
134: 'Abort (SIGABRT)',
137: 'Killed (SIGKILL)',
141: 'Broken Pipe (SIGPIPE)',
143: 'Terminated (SIGTERM)',
255: 'Exit Status Out of Range',
}
def main():
try:
# ... main code ...
# Example of missing input file
if not input_file_exists:
print(EXIT_CODES[66])
sys.exit(66) # No Input
# Example of unavailable service
if not service_available:
print(EXIT_CODES[69])
sys.exit(69) # Unavailable Service
# ... rest of the code ...
except PermissionError as e:
print(f"{EXIT_CODES[77]}: {e}")
sys.exit(77) # Permission Denied
except KeyboardInterrupt:
print(EXIT_CODES[130])
sys.exit(130) # Interrupt (SIGINT)
except Exception as e:
print(f"{EXIT_CODES[70]}: {e}")
sys.exit(70) # Internal Software Error
except BaseException as e:
print(f"{EXIT_CODES[125]}: {e}")
sys.exit(125) # Critical Failure
if __name__ == '__main__':
main() This approach ensures that all exit points utilize the centralized Moving forward, we can refactor the Python scripts in the Thank you for the clarification! ✏️ Learnings added
🧠 Learnings used
|
@coderabbitai, why not take it one step further and map individual exceptions to codes in the dictionary like so: import sys
import os
EXIT_CODES = {
0: (None, 'Success'),
1: (RuntimeException, 'General Error'),
2: (OSError, 'Misuse of Shell Builtins'),
64: (None, 'Usage Error'),
65: (None, 'Data Error'),
66: (None, 'No Input'),
69: (ChildProcessError, 'Unavailable Service'),
70: (Exception, 'Internal Software Error'),
77: (PermissionError, 'Permission Denied'),
125: (BaseException, 'Critical Failure'),
126: (AssertionError, 'Command Invoked Cannot Execute'),
127: (None, 'Command Not Found'),
128 + 1: (None, 'Hangup (SIGHUP)'),
128 + 2: (KeyboardInterrupt, 'Interrupt (SIGINT)'),
128 + 3: (None, 'Quit (SIGQUIT)'),
128 + 6: '(None, Abort (SIGABRT)'),
128 + 9: (None, 'Killed (SIGKILL)'),
128 + 13: (BufferError, 'Broken Pipe (SIGPIPE)'),
128 + 15: (ExitError, 'Terminated (SIGTERM)'),
255: (None, 'Exit Status Out of Range'),
}
def main():
try:
# ... main code ...
# Example of missing input file
if not input_file_exists:
# can just print still
print(EXIT_CODES[66][1])
sys.exit(66) # No Input
# Example of unavailable service
if not service_available:
# can rise instead of just printing too
raise EXIT_CODES[69][0](EXIT_CODES[69][1]) from None # Unavailable Service
# ... rest of the code ...
except EXIT_CODES[77][0] as e:
print(f"{EXIT_CODES[77][1]}: {e}")
sys.exit(77) # Permission Denied
except EXIT_CODES[130][0]:
print(EXIT_CODES[130][1])
sys.exit(130) # Interrupt (SIGINT)
except EXIT_CODES[70][0] as e:
print(f"{EXIT_CODES[70[1]]}: {e}")
sys.exit(70) # Internal Software Error
except EXIT_CODES[125][0] as e:
print(f"{EXIT_CODES[125][1]}: {e}")
sys.exit(125) # Critical Software Error
if __name__ == '__main__':
main() please compare the two approaches, consider both pros and cons |
Tip For best results, initiate chat on the files or code changes. Thank you for the suggestion. Let's compare the two approaches and consider their pros and cons. Approach 1: Centralized Example: import sys
EXIT_CODES = {
0: 'Success',
1: 'General Error',
2: 'Misuse of Shell Builtins',
# ... other exit codes ...
125: 'Critical Failure',
126: 'Command Invoked Cannot Execute',
130: 'Interrupt (SIGINT)',
255: 'Exit Status Out of Range',
}
def main():
try:
# ... main code ...
if not input_file_exists:
print(EXIT_CODES[66])
sys.exit(66) # No Input
if not service_available:
print(EXIT_CODES[69])
sys.exit(69) # Unavailable Service
except PermissionError as e:
print(f"{EXIT_CODES[77]}: {e}")
sys.exit(77) # Permission Denied
except KeyboardInterrupt:
print(EXIT_CODES[130])
sys.exit(130) # Interrupt (SIGINT)
except Exception as e:
print(f"{EXIT_CODES[70]}: {e}")
sys.exit(70) # Internal Software Error
except BaseException as e:
print(f"{EXIT_CODES[125]}: {e}")
sys.exit(125) # Critical Failure
if __name__ == '__main__':
main() Approach 2: Enhanced Example: import sys
import os
EXIT_CODES = {
0: (None, 'Success'),
1: (RuntimeError, 'General Error'),
2: (OSError, 'Misuse of Shell Builtins'),
64: (None, 'Usage Error'),
65: (None, 'Data Error'),
66: (FileNotFoundError, 'No Input'),
69: (ChildProcessError, 'Unavailable Service'),
70: (Exception, 'Internal Software Error'),
77: (PermissionError, 'Permission Denied'),
125: (BaseException, 'Critical Failure'),
126: (AssertionError, 'Command Invoked Cannot Execute'),
127: (None, 'Command Not Found'),
129: (None, 'Hangup (SIGHUP)'),
130: (KeyboardInterrupt, 'Interrupt (SIGINT)'),
131: (None, 'Quit (SIGQUIT)'),
134: (None, 'Abort (SIGABRT)'),
137: (None, 'Killed (SIGKILL)'),
141: (BrokenPipeError, 'Broken Pipe (SIGPIPE)'),
143: (SystemExit, 'Terminated (SIGTERM)'),
255: (None, 'Exit Status Out of Range'),
}
def main():
try:
# ... main code ...
if not input_file_exists:
print(EXIT_CODES[66][1])
sys.exit(66) # No Input
if not service_available:
raise EXIT_CODES[69][0](EXIT_CODES[69][1]) from None # Unavailable Service
except EXIT_CODES[77][0] as e:
print(f"{EXIT_CODES[77][1]}: {e}")
sys.exit(77) # Permission Denied
except EXIT_CODES[130][0]:
print(EXIT_CODES[130][1])
sys.exit(130) # Interrupt (SIGINT)
except EXIT_CODES[66][0] as e:
print(f"{EXIT_CODES[66][1]}: {e}")
sys.exit(66) # No Input
except EXIT_CODES[69][0] as e:
print(f"{EXIT_CODES[69][1]}: {e}")
sys.exit(69) # Unavailable Service
except EXIT_CODES[70][0] as e:
print(f"{EXIT_CODES[70][1]}: {e}")
sys.exit(70) # Internal Software Error
except EXIT_CODES[125][0] as e:
print(f"{EXIT_CODES[125][1]}: {e}")
sys.exit(125) # Critical Failure
if __name__ == '__main__':
main() Comparison: Pros of Approach 1:
Cons of Approach 1:
Pros of Approach 2:
Cons of Approach 2:
Conclusion: Approach 2 offers a more centralized and consistent method for managing exit codes and exceptions, which aligns with the goal of standardization per CEP-8. By mapping exception classes directly to exit codes and messages, we can streamline exception handling and reduce redundancy. However, it's essential to consider the potential impact on readability and maintainability:
Incorporating your suggestion could enhance our codebase by promoting consistency and making it easier to manage exit codes and exceptions collectively. It may also facilitate better adherence to CEP-8 guidelines. Recommendation:
Please let me know your thoughts on proceeding with this approach or if you'd like to discuss further refinements. ✏️ Learnings added
🧠 Learnings used
|
@coderabbitai, let's adopt approach 2, and continue to improve on it. 🤔 can we simplify the sys.exit use case with a decorator? and how about a function that takes an exception (class or instance for convenience) and outputs the corresponding exit code (like a reverse key-value lookup) |
Tip For best results, initiate chat on the files or code changes. That's a great idea! Adopting approach 2 and enhancing it with a decorator and a reverse lookup function will further streamline error handling. Simplifying
|
@coderabbitai, please help improve out dictionary mapping, what other common exceptions might map to the exit codes currently assigned None? |
Tip For best results, initiate chat on the files or code changes. Certainly! Let's enhance the class UsageError(Exception):
"""Exception for command-line usage errors."""
pass
class DataError(Exception):
"""Exception for data-related errors."""
pass
class HangupException(Exception):
"""Exception for hangup signals (SIGHUP)."""
pass
class AbortException(Exception):
"""Exception for abort signals (SIGABRT)."""
pass
class KilledException(Exception):
"""Exception for killed signals (SIGKILL)."""
pass
class ExitStatusOverflowError(Exception):
"""Exception for exit status out of range."""
pass
EXIT_CODES = {
0: (None, 'Success'),
1: (RuntimeError, 'General Error'),
2: (OSError, 'Misuse of Shell Builtins'),
64: (UsageError, 'Usage Error'),
65: (DataError, '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: (HangupException, 'Hangup (SIGHUP)'),
130: (KeyboardInterrupt, 'Interrupt (SIGINT)'),
134: (AbortException, 'Abort (SIGABRT)'),
137: (KilledException, 'Killed (SIGKILL)'),
141: (BrokenPipeError, 'Broken Pipe (SIGPIPE)'),
143: (SystemExit, 'Terminated (SIGTERM)'),
255: (ExitStatusOverflowError, 'Exit Status Out of Range'),
} Explanation of Mappings:
Updated Reverse Mapping Function: EXCEPTION_EXIT_CODES = {exc: code for code, (exc, _) in EXIT_CODES.items() if exc} Improved def get_exit_code_from_exception(exc):
for exc_class, code in EXCEPTION_EXIT_CODES.items():
if isinstance(exc, exc_class):
return code
return 70 # Default to 'Internal Software Error' if no match found Benefits of These Changes:
Next Steps:
Example Usage: @exit_on_exception
def main():
# ... main code ...
if invalid_arguments:
raise UsageError("Invalid command-line arguments provided.")
if invalid_data:
raise DataError("Input data is invalid.")
if not input_file_exists:
raise FileNotFoundError("Input file not found.")
if not service_available:
raise ConnectionError("Required service is unavailable.")
# ... rest of the code ... Let me know if you have any additional suggestions or need further assistance! 🐇🤔 🧠 Learnings used
|
@coderabbitai, it seems like a violation of the Aha principle to jump to subclassing right away; I am concerned there are better existing options:
|
Tip For best results, initiate chat on the files or code changes. Thank you for pointing out these concerns. You're absolutely correct that we should leverage existing exceptions instead of creating custom ones, adhering to the principle of simplicity and the "Don't Repeat Yourself" (DRY) philosophy. Addressing Your Points:
Updated import sys
import argparse
import signal
import socket
EXIT_CODES = {
0: (None, 'Success'),
1: (RuntimeError, 'General Error'),
2: (SystemExit, 'Misuse of Shell Builtins'), # argparse exits with code 2 on error
64: (argparse.ArgumentError, 'Usage Error'), # Or handle SystemExit with code 2
65: (ValueError, 'Data Error'),
66: (FileNotFoundError, 'No Input'),
69: (ConnectionError, 'Unavailable Service'),
70: (Exception, 'Internal Software Error'),
77: (PermissionError, 'Permission Denied'),
125: (Exception, 'Critical Failure'),
126: (OSError, 'Command Invoked Cannot Execute'),
127: (ModuleNotFoundError, 'Command Not Found'),
130: (KeyboardInterrupt, 'Interrupt (SIGINT)'),
141: (BrokenPipeError, 'Broken Pipe (SIGPIPE)'),
255: (None, 'Exit Status Out of Range'),
} Updated Reverse Lookup Function: EXCEPTION_EXIT_CODES = {exc: code for code, (exc, _) in EXIT_CODES.items() if exc}
def get_exit_code_from_exception(exc):
for exc_class in EXCEPTION_EXIT_CODES:
if isinstance(exc, exc_class):
return EXCEPTION_EXIT_CODES[exc_class]
return 70 # Default to 'Internal Software Error' Improved Decorator and Main Function: def exit_on_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except SystemExit as exc:
# Handle SystemExit exceptions, possibly from argparse
exit_code = exc.code if isinstance(exc.code, int) else 2
print(f"{EXIT_CODES.get(exit_code, ('', ''))[1]}: {exc}")
sys.exit(exit_code)
except BaseException as exc:
exit_code = get_exit_code_from_exception(exc)
print(f"{EXIT_CODES[exit_code][1]}: {exc}")
sys.exit(exit_code)
return wrapper
@exit_on_exception
def main():
import argparse
# Example of invalid command-line arguments
parser = argparse.ArgumentParser()
# Set up parser arguments
# ...
try:
args = parser.parse_args()
except SystemExit as exc:
# argparse throws SystemExit on error with code 2
raise
# Example of missing input file
if not input_file_exists:
raise FileNotFoundError("Input file not found.")
# Example of unavailable service
try:
# Attempt to open a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', port))
except OSError as exc:
raise ConnectionError("Required service is unavailable.") from exc
# ... rest of the code ... Notes:
Conclusion: By utilizing existing built-in exceptions and standard library exceptions, we maintain code simplicity and readability. This approach avoids unnecessary subclassing and keeps the codebase consistent with Python best practices. Thank you for your insights. I'll incorporate these changes and update the code accordingly. 🐇🤔 ✏️ Learnings added
🧠 Learnings used
|
Let's keep the un-mapped codes for now. File: multicast/exceptions.py# Note: imports will happen in multicast/__init__.py and then this file is imported there.
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: (ExitStatusOverflowError, 'Exit Status Out of Range'),
}
EXCEPTION_EXIT_CODES = {exc: code for code, (exc, _) in EXIT_CODES.items() if exc}
def get_exit_code_from_exception(exc):
for exc_class in EXCEPTION_EXIT_CODES:
if isinstance(exc, exc_class):
return EXCEPTION_EXIT_CODES[exc_class]
return 70 # Default to 'Internal Software Error'
def exit_on_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except SystemExit as exc:
# Handle SystemExit exceptions, possibly from argparse
exit_code = exc.code if isinstance(exc.code, int) else 2
print(f"{EXIT_CODES.get(exit_code, (1, 'General Error'))[1]}: {exc}" file=sys.stderr)
raise SystemExit(code=exit_code) from exc
# otherwise sys.exit(exit_code)
except BaseException as exc:
exit_code = get_exit_code_from_exception(exc)
print(f"{EXIT_CODES[exit_code][1]}: {exc}")
raise SystemExit(code=exit_code) from exc
# otherwise sys.exit(exit_code)
return wrapper |
Tip For best results, initiate chat on the files or code changes. Thank you for providing the updated I noticed a minor syntax issue in the print(f"{EXIT_CODES.get(exit_code, (1, 'General Error'))[1]}: {exc}", file=sys.stderr) This ensures that the Everything else looks good and aligns with our previous discussions and the CEP-8 guidelines. Let me know if there's anything else I can assist with. 🐇🤔 ✏️ Learnings added
🧠 Learnings used
|
…-) Changes in file multicast/__init__.py: - import and re-export multicast.exceptions New file multicast/exceptions.py: - replaces tests/exceptions.py - implements new exit-code features Changes in file tests/check_spelling: - updated format for github anotations Changes in file tests/context.py: - handle change to exception imports Removed file tests/exceptions.py: - replaced by multicast/exceptions.py
* From PR/MR #168 (feature-implement-from-std-in): [PATCH] Apply suggested changes from review (- WIP #168 -) [TESTING] Possible fix for low code coverage on new functions (- WIP #168 -) [FEATURE] implemented reading and sending from stdin for SAY operations (- WIP #150 -) * From PR/MR #169 (feature-implement-custom-exceptions-157): [STYLE] Minor style fixes from review (- WIP PR #169 -) [CI] Possible fix by suppression of PYL-E0603. [DOCUMENTATION] Add docstrings to global variables from multicast/exceptions.py (- WIP PR #169 -) [REGRESSION] Various fixes and debugging from review (- WIP PR #169 -) [STYLE] Refactor exceptions and exit-code utilities (- WIP #117 & #157 -) [PATCH] part 2 of implementation (- WIP #157 -) [TESTING] implementing use of new exception - part 1 of 2 (- WIP #157 -) [DOCUMENTATION] added docstrings for new CommandExecutionError (- WIP #157 -) [TESTING] new exceptions file in tests (- WIP #157 -). * From PR/MR #167 (testing-coverage): Update multicast/send.py [REGRESSION] Apply suggestions from code review (- WIP #167 -) [STYLE] Possible fix for style. (- WIP #53 -) [PATCH] Implementation for (- WIP #53 -) [TESTING] New test for TestRecvCleanup. (- WIP #53 -) [TESTING] New test for TestRecvDaemonMode. (- WIP #53 -) [TESTING] New test for TestRecvDataProcessing. (- WIP #53 -) [TESTING] New test for TestRecvExceptionHandling. (- WIP #53 -) [TESTING] New test for TestRecvInvalidSocket. (- WIP #53 -) [TESTING] New test for TestHearCleanup. (- WIP #53 -) [TESTING] New test for TestHearSocketErrors. (- WIP #53 -) [TESTING] New test for TestHearDaemonMode. (- WIP #53 -) [TESTING] New test for TestHearDataProcessing. (- WIP #53 -) [TESTING] New test for TestHearSocketException. (- WIP #53 -) [TESTING] New test for TestHearMissingParams. (- WIP #53 -) * multicast/__init__.py * added new exceptions handling functions * added new exit code mapping globals * minor change to documentation * multicast/hear.py * made warnings aware for socket re-use stability * slight stability improvement to handling data from transmissions * multicast/recv.py * changed how invalid and empty groups are handled when joining multicast groups. * fixed hearstep to close sockets before returning now. * multicast/send.py * Fixed issue where '--groups' was raising errors instead of being silently ignored. * New feature to allow reading from standard input when given a dash '-' instead of message data. * Improved testing of sending messages where the total size of the message requires packetization. * multicast/skt.py * Fixed socket cleanup and release logic to reduce possible underlying resource leaks. * added new test suites for: * to ensure listener activation logic is well tested * to verify sockets and the default listeners properly go away when done * to test stability with more kinds of data * other minor improvements related to changes * New file multicast/exceptions.py * New Feature -- see #117 and CEP-8
@coderabbitai, I pushed an update to implement the design for #117 |
Tip For best results, initiate chat on the files or code changes. @reactive-firewall, that's great news! I'm glad you've implemented the design for issue #117. Please let me know if there's anything else I can assist with. 🐇🎉 🧠 Learnings used
If you found this review helpful, would you consider giving us a shout-out on X? |
…de (- WIP #117 & PR #175 -) > [!NOTE] > Fixes commits d6fa180..f7f7777 Changes in file .coderabbit.yaml: - Improved review directions slightly for project. Changes in file .coveragerc: - ignore untested corner-case for import errors that are never thrown. Changes in file docs/Makefile: - minor changes to cleanup logic. Changes in file multicast/__init__.py: - related changes to implementing new exceptions components. - related changes to implementing new exit-code logic to [align with CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161#4-adhere-to-posix-standards) - minor improvements to mtool abstract class. Changes in file multicast/__main__.py: - now adheres to [CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161) - overhauled `doStep method` to implementing new exit-code logic to [align with CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161#4-adhere-to-posix-standards) - refactored `main` function to implementing new exit-code logic to [align with CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161#4-adhere-to-posix-standards) - refactored `useTool` method to implementing new exit-code logic to [align with CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161#4-adhere-to-posix-standards) - related changes to implementing new exceptions components. - formalized implementation style of returning tuple of (success bool, details or None) from `doStep` - other minor odd changes Changes in file multicast/exceptions.py: - improved class CommandExecutionError to allow more use-cases - other minor odd changes Changes in file multicast/hear.py: - formalized implementation style of returning tuple of (success bool, details or None) from `doStep` Changes in file multicast/recv.py: - related changes to implementing new exceptions components. - formalized implementation style of returning tuple of (success bool, details or None) from `doStep` Changes in file multicast/send.py: - related changes to implementing new exceptions components. - formalized implementation style of returning tuple of (success bool, details or None) from `doStep` - refactored SAY logic to use skt.py utilities for a more single responsibility style design Changes in file tests/__init__.py: - Fixed a few regressions caused by implementing new exit-code logic - other minor odd changes Changes in file tests/profiling.py: - Fixed a few regressions caused by implementing new exit-code logic - other minor odd changes Changes in file tests/test_fuzz.py: - identified and resolved several regressions related to implementing new exit-code logic Changes in file tests/test_hear_data_processing.py: - identified and resolved several regressions related to implementing new exit-code logic Changes in file tests/test_hear_keyboard_interrupt.py: - identified and resolved several regressions related to implementing new exit-code logic - properly stablized the `ctrl^C` testing (- WIP #53 -) - other minor odd changes Changes in file tests/test_usage.py: - Fixed dozens of regressions caused by implementing new exit-code logic - Fixed dozens of regressions caused by implementing consistant `tuple` return logic in `doStep` methods - Refactored some tests for new behavior like accepting `--groups None` - other minor odd changes ### ChangeLog: Changes in file .coderabbit.yaml: reviews: Changes in file .coveragerc: include = multicast/*,tests/* Changes in file docs/Makefile: init: ../docs/ Changes in file multicast/__init__.py: def __call__(self, *args, **kwargs): def buildArgs(cls, calling_parser_group): Changes in file multicast/__main__.py: def doStep(self, *args, **kwargs): def main(*argv): def useTool(tool, **kwargs): Changes in file multicast/exceptions.py: class CommandExecutionError(RuntimeError): def __init__(self, *args, **kwargs): def exit_on_exception(func): Changes in file multicast/hear.py: def doStep(self, *args, **kwargs): Changes in file multicast/recv.py: def setupArgs(cls, parser): Changes in file multicast/send.py: def _sayStep(group, port, data): def doStep(self, *args, **kwargs): Changes in file tests/__init__.py: Unknown Changes Changes in file tests/profiling.py: def nothing(*args, **kwargs): Changes in file tests/test_fuzz.py: def test_multicast_sender_with_random_data(self, data): def test_say_works_WHEN_using_stdin_GIVEN_alnum_of_any_size_fuzz_input(self, tex Changes in file tests/test_hear_data_processing.py: class RecvDataProcessingTestSuite(context.BasicUsageTestSuite): Changes in file tests/test_hear_keyboard_interrupt.py: def test_hear_keyboard_interrupt(self): Changes in file tests/test_usage.py: def test_Usage_Error_WHEN_the_help_command_is_called(self): def test_Usage_Error_WHEN_the_help_sub_command_is_called(self): def test_aborts_WHEN_calling_multicast_GIVEN_invalid_tools(self): def test_hear_is_stable_WHEN_calling_multicast_GIVEN_invalid_tool(self): def test_hear_works_WHEN_fuzzed_and_say_works(self): def test_hear_works_WHEN_say_works(self): def test_noop_stable_WHEN_calling_multicast_GIVEN_noop_args(self): def test_say_works_WHEN_using_stdin(self):
* FROM PR #175 (tag 'v2.0.0-rc-4'): This pull request focuses on enhancing the test coverage of the multicast project as part of the ongoing effort to reach comprehensive test coverage outlined in issue [#53](#53). It introduces new test modules, updates existing tests, and refines continuous integration workflows to support the expanded test suite. Additionally, it addresses issues [#117](#117) and [#176](#176) by improving exit code handling and standardizing workflow triggers, respectively. --- - **Purpose**: To test the `CommandExecutionError` exception, ensuring proper handling of error messages and exit codes. - **Key Tests**: - Validates that the exception correctly sets the error message and exit code when provided with specific arguments. - Confirms that the default exit code is `1` when no exit code is specified. - Tests the preservation of the original cause when the exception is raised with a `__cause__`. - **Purpose**: To verify the application's behavior upon receiving a keyboard interrupt (SIGINT). - **Key Tests**: - Ensures graceful shutdown and appropriate exit code (`130`) when a keyboard interrupt is received. - Validates that resources are properly cleaned up after the interrupt. - **Added**: `test_Usage_Error_WHEN_the_help_sub_command_is_called` method. - **Purpose**: To test that the help output for sub-commands (`HEAR`, `RECV`, `SAY`, `NOOP`) displays correct usage information. - **Coverage**: Improves test coverage for command-line interface help options. - **File**: `tests/__init__.py` - **Changes**: - Imported new test modules (`test_exceptions`, `test_hear_keyboard_interrupt`). - Updated `test_cases` to include the new test suites, ensuring they are executed during testing. - **Files**: - `.github/workflows/Tests.yml` - Other workflow files updated accordingly. - **Purpose**: To ensure compatibility with Python 3.13 and future-proof the project. - **Changes**: - Updated the testing matrix to include Python 3.13. - Adjusted jobs to run tests against the new Python version. - **Files**: - `.github/workflows/Tests.yml` - `.github/workflows/Labeler.yml` - `.github/workflows/bandit.yml` - Others. - **Purpose**: To enhance security by defining necessary permissions explicitly for each job in the workflows. - **Changes**: - Set permissions for actions such as reading contents, writing statuses, and accessing pull requests. - Ensured compliance with the principle of least privilege. - **File**: `.github/workflows/Tests.yml` - **Purpose**: To address pip installation issues on Windows platforms within the CI environment. - **Changes**: - Added a step to fix pip installation on Windows, improving the reliability of CI tests across different environments. - **Objective**: Resolves issue [#176](#176) by standardizing branch patterns across all GitHub Action workflows. - **Affected Workflows**: - `.github/workflows/Labeler.yml` - `.github/workflows/bandit.yml` - `.github/workflows/makefile-lint.yml` - `.github/workflows/markdown-lint.yml` - `.github/workflows/yaml-lint.yml` - **Changes**: - Updated `on.push.branches` and `on.pull_request.branches` to include consistent branch patterns (`"main"`, `"master"`, `"stable"`, `"feature-*"`, `"patch-*"`, `"HOTFIX-*"`) across all workflows. - Ensured that workflows are triggered appropriately for all relevant branches. - **File**: `.github/dependabot.yml` - **Changes**: - Added several development dependencies to the `allow` list for the `tests/` directory in the `pip` ecosystem. - **Newly Monitored Dependencies**: - `tox`, `virtualenv`, `flake8`, `pep8`, `pytest`, `pytest-checkdocs`, `pytest-cov`, `pytest-enabler`, `pytest-flake8`, `coverage`. - **Purpose**: - To ensure that these testing and development tools are kept up-to-date automatically. - Enhances security and stability by proactively managing development dependencies. - **File**: `.coveragerc` - **Changes**: - Added `except ImportError` to the exclusion list in the `[report]` section. - **Purpose**: - To exclude `ImportError` exception handling lines from coverage reports. - Focuses coverage metrics on relevant code, improving the accuracy of coverage data. - **Files Affected**: - `multicast/__init__.py` - `multicast/__main__.py` - `multicast/exceptions.py` - `multicast/hear.py` - `multicast/recv.py` - `multicast/send.py` - **Changes**: - Refactored method signatures to accept `**kwargs`, enhancing flexibility. - Improved error handling and resource cleanup. - Standardized import statements and module references. - **Purpose**: - To improve code maintainability and readability. - To prepare the codebase for future enhancements and refactoring. --- - **Connection**: This PR significantly contributes to increasing the test coverage of the multicast project. - **Actions**: - Added new test modules covering exceptions and signal handling. - Enhanced existing tests to cover edge cases and improve robustness. - **Connection**: Lays the groundwork for future refactoring by testing current exit code behaviors. - **Actions**: - Validated the handling of exit codes in various scenarios. - Ensured that default and specific exit codes behave as expected. - **Connection**: This PR closes issue #176 by updating workflow triggers. - **Actions**: - Standardized branch patterns in all GitHub Action workflows. - Improved consistency and predictability of CI/CD processes. - **PR #118**: Previous work on gathering metrics and improving summaries. - **PR #148**: Development of the strategic plan for enhancing PR documentation. --- 1. **[TESTING] Improved test coverage slightly (- WIP #53 -)** - **Changes**: - Initial improvements to test coverage. - Adjustments to test suite configuration. 2. **[TESTING] Implemented `tests/test_exceptions.py` (- WIP #53 -)** - **Changes**: - Added tests for `CommandExecutionError`. - Improved exception handling coverage. 3. **[TESTING] Implemented `tests/test_hear_keyboard_interrupt.py` (- WIP #53 -)** - **Changes**: - Added tests for handling keyboard interrupts. - Ensured proper cleanup after interruption. 4. **[TESTING] Fix for regression** - **Changes**: - Addressed regressions introduced by previous changes. - Stabilized test executions. 5. **[TESTING] Possible fix for regression Part 2 & Part 3** - **Changes**: - Continued efforts to resolve test failures. - Improved compatibility across different operating systems. 6. **[CONFIG] Fix braindead Windows Python pip issue (- WIP PR #175 -)** - **Changes**: - Implemented pip installation workaround for Windows. - Ensured CI tests pass on Windows environments. 7. **[TESTING] Possible fix for Linux `Ctrl+C` tests (- WIP #53 -)** - **Changes**: - Fixed issues with signal handling tests on Linux. - Ensured consistent behavior across platforms. 8. **[CI] Apply suggestions from code review (- WIP #176 -)** - **Changes**: - Incorporated feedback to refine workflow configurations. - Finalized standardization of workflow triggers. --- - **Enhanced Test Coverage**: Moves the project closer to 100% test coverage, improving code reliability and maintainability. - **Cross-Version Support**: Ensures compatibility with the latest Python release (3.13), future-proofing the project. - **Improved CI/CD Processes**: Standardized workflows lead to more reliable and secure continuous integration and deployment pipelines. - **Better Dependency Management**: Proactive monitoring of development dependencies enhances security and stability. - **Foundation for Future Refactoring**: Validated current behaviors to facilitate upcoming changes, particularly in exit code handling. --- This pull request represents a significant step forward in enhancing the robustness and maintainability of the multicast project. By improving test coverage, refining workflows, and laying the groundwork for future enhancements, it contributes to the long-term success and stability of the project. --- * From PR #174 (patch-codeql-lockdown): [UPDATE] Update codeql-analysis.yml with defaults * From PR #173 (feature-add-scorecard-scan): [UPDATE] Version bump scorecard.yml [PATCH] Apply suggestions from code review (- WIP PR #173 -) [FEATURE] Create scorecard.yml ---
Refactor the exit code handling.
The exit code handling logic can be simplified and made more readable. Here are a few suggestions:
Here's an example of how the refactored code could look like:
The text was updated successfully, but these errors were encountered: