如何为调用Perl脚本的Python subprocess添加正确异常处理?
Hey there! Let's walk through how to add robust exception handling to your Decrypt class's decrypt_file method. The subprocess module can fail in a few key ways, so we'll cover those scenarios while also adding some quality-of-life improvements to your code.
Here's the updated version with comprehensive error handling:
import subprocess class Decrypt: def __init__(self, password): self.password = password def decrypt_file(self): command = "perl -e 'require \"Utilities.pm\"; print my $pass = Utilities::Decrypt(\"E\", \"{}\")';".format(self.password) try: # Execute the command, capturing both stdout and stderr process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True # Auto-decode output to strings instead of raw bytes ) output, error = process.communicate() # Check if the Perl script exited with a failure status code if process.returncode != 0: raise subprocess.CalledProcessError( returncode=process.returncode, cmd=command, output=output, stderr=error ) return output.strip() # Clean up extra whitespace/newlines from the result except OSError as e: # Handle cases where the system can't run the command (e.g., Perl not installed) raise RuntimeError(f"Failed to launch the command: {str(e)}") from e except subprocess.CalledProcessError as e: # Handle failures in the Perl script itself raise RuntimeError( f"Perl decryption failed with exit code {e.returncode}. Error details: {e.stderr}" ) from e except UnicodeDecodeError as e: # Handle issues decoding the command's output/error text raise RuntimeError(f"Failed to read command output: {str(e)}") from e
Breakdown of the Exception Handling:
OSError: Catches fundamental system-level failures, like if Perl isn't installed on the machine, or the shell can't locate the executable. Usingraise ... from epreserves the original error context so you can trace exactly what went wrong.subprocess.CalledProcessError: Even if the command runs, we explicitly check the exit code. If the Perl script returns a non-zero value (indicating failure), we raise this error and include the stderr output—this is critical for debugging issues like missingUtilities.pmor invalid password input.UnicodeDecodeError: By usingtext=True, we tellsubprocessto return strings instead of bytes. If the output contains invalid Unicode characters, this exception catches the problem before it crashes your code.
Bonus Improvements:
- Added
stderr=subprocess.PIPEto capture error messages from the Perl script—this makes debugging decryption failures way easier. - Used
text=Trueto avoid manual byte decoding (no more needing to calloutput.decode()). - Added
strip()to clean up any trailing newlines or whitespace from the final decrypted output.
A Quick Security Note:
Since you're using shell=True with user-provided password input, be aware of shell injection risks. If the password can contain special characters (like ;, &, or $), your current command formatting could let an attacker run arbitrary system commands. At minimum, you should escape special characters in the password before inserting it into the Perl command string.
内容的提问来源于stack exchange,提问作者JanFi86

