I have a sample python3 code below, and want to capture what kinds of error when calling psutil functions. However after running the code below, it only prints out the Error: <class 'psutil.Error'>.
How can I capture the real meaningful error message here? Thanks.
import psutil
pid = 12345
try:
p = psutil.Process(pid)
p.terminate()
except psutil.Error:
print("Error: ", psutil.Error)
You need to assign the error to a variable on the end of your except statement, like this:
import psutil
pid = 12345
try:
p = psutil.Process(pid)
p.terminate()
except psutil.Error as error:
stringerror = str(error)
print ("Error: " + stringerror)
I've also converted the error statement variable to a string at the end so the output here is as you intended, this avoids a TypeError with concantenation.
I have a little script that telnets into a network switch and executes commands. It works fine but I need it stop and show an error message if any of the commands fail or network give a waning or something.
Here is the code:
import sys
import telnetlib
HOST = "10.10.10.1"
user = "Test"
password = "TestPW"
tn = telnetlib.Telnet(HOST)
tn.read_until("username: ")
tn.write(user + "\n")
tn.read_until("password: ")
tn.write(password + "\n")
n, match, previous_text = tn.expect([r'Login incorrect', r'\$'], 1)
if n == 0:
print "Invalid Credentials"
tn.close()
else:
tn.write("Term len 0\n")
#Reads data from commands.txt file and executes it line by line
with open("commands.txt") as commands:
singlecommand = commands.read()
tn.write(singlecommand)
print singlecommand
#Need exception/error checking to catch fail commands or warnings.
tn.write("exit\n")
tn.write("y\n")
print tn.read_all()
tn.close()
I want the script to stop executing more commands after a fail command or a warning from CLI that something maybe wrong. It already has the print function so it should display the error message and command that failed.
Here is a example of a failed command:
% Invalid input detected at '^' marker.
Here is an example warning message:
%Warning:
You can use Try/Except statements.
They're related to the Errors and Exceptions part of the Python docs.
Example:
try:
wrong_sum = "a" + 2
except ValueError:
print "You got a Value Error"
else:
break
you can extract the message based on key word appeared in failed command, then raise the exception.
for example:
try:
if '%Warning' == message[:8]:
raise Exception('Test')
except Exception:
print message
I would like to ask if there is any solution to handle unsupported system command in Python. E.g.:
import os
os.system("svn")
In this code I tried to use svn command and I get an error:
"svn" is not recognized as an internal or external comand...
It's obvious, because I didn't install svn, that's why I have got this error etc. The question is not about "svn", but in general command which is not supported by opereting system. Is there any possiblity to handle such event in Python and if such situation occurs there will be info that this command is unsupported by your system.
os.system returns many different codes upon completion but subprocess.call can be caught.
try:
subprocess.call(['svn'])
except OSError:
print 'an error occurred'
I believe os.system is deprecated, see this page for subprocess: https://docs.python.org/2/library/subprocess.html#replacing-older-functions-with-the-subprocess-module
With the following code:
import subprocess
import sys
try:
retcode = subprocess.call("svn")
if retcode < 0:
print >>sys.stderr, "Child was terminated by signal", -retcode
else:
print >>sys.stderr, "Child returned", retcode
except OSError as e:
print >>sys.stderr, "Execution failed:", e
You get error:
Execution failed: [Errno 2] No such file or directory
Use subprocess.check_call, catching a CalledProcessError and a FileNotFoundError:
from subprocess import check_call, CalledProcessError
try:
check_call(["svn"])
except CalledProcessError as e:
print("There was an error calling {}".format(e.cmd))
except FileNotFoundError as e:
print("Invalid command")
It will also raise an error if the command returns a non-zero exit status.
The os.system command is depreciated as are os.popen etc.. You should always use the subprocess module.
I'm trying to do a Bitcoin payment from within Python. In bash I would normally do this:
bitcoin sendtoaddress <bitcoin address> <amount>
So for example:
bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214
If it is successful I get a transaction id as output, but if I try to transfer an amount larger than my bitcoin balance, I get the following output:
error: {"code":-4,"message":"Insufficient funds"}
In my Python program I now try to do the payment as follows:
import subprocess
try:
output = subprocess.check_output(['bitcoin', 'sendtoaddress', address, str(amount)])
except:
print "Unexpected error:", sys.exc_info()
If there's enough balance it works fine, but if there's not enough balance sys.exc_info() prints out this:
(<class 'subprocess.CalledProcessError'>, CalledProcessError(), <traceback object at 0x7f339599ac68>)
It doesn't include the error which I get on the command line though. So my question is; how can I get the outputted error ({"code":-4,"message":"Insufficient funds"}) from within Python?
According to the subprocess.check_output() docs, the exception raised on error has an output attribute that you can use to access the error details:
try:
subprocess.check_output(...)
except subprocess.CalledProcessError as e:
print(e.output)
You should then be able to analyse this string and parse the error details with the json module:
if e.output.startswith('error: {'):
error = json.loads(e.output[7:]) # Skip "error: "
print(error['code'])
print(error['message'])
I don't think the accepted solution handles the case where the error text is reported on stderr. From my testing the exception's output attribute did not contain the results from stderr and the docs warn against using stderr=PIPE in check_output(). Instead, I would suggest one small improvement to J.F Sebastian's solution by adding stderr support. We are, after all, trying to handle errors and stderr is where they are often reported.
from subprocess import Popen, PIPE
p = Popen(['bitcoin', 'sendtoaddress', ..], stdout=PIPE, stderr=PIPE)
output, error = p.communicate()
if p.returncode != 0:
print("bitcoin failed %d %s %s" % (p.returncode, output, error))
As mentioned by #Sebastian the default solution should aim to use run():
https://docs.python.org/3/library/subprocess.html#subprocess.run
Here a convenient implementation (feel free to change the log class with print statements or what ever other logging functionality you are using):
import subprocess
def _run_command(command):
log.debug("Command: {}".format(command))
result = subprocess.run(command, shell=True, capture_output=True)
if result.stderr:
raise subprocess.CalledProcessError(
returncode = result.returncode,
cmd = result.args,
stderr = result.stderr
)
if result.stdout:
log.debug("Command Result: {}".format(result.stdout.decode('utf-8')))
return result
And sample usage (code is unrelated, but I think it serves as example of how readable and easy to work with errors it is with this simple implementation):
try:
# Unlock PIN Card
_run_command(
"sudo qmicli --device=/dev/cdc-wdm0 -p --uim-verify-pin=PIN1,{}"
.format(pin)
)
except subprocess.CalledProcessError as error:
if "couldn't verify PIN" in error.stderr.decode("utf-8"):
log.error(
"SIM card could not be unlocked. "
"Either the PIN is wrong or the card is not properly connected. "
"Resetting module..."
)
_reset_4g_hat()
return
Trying to "transfer an amount larger than my bitcoin balance" is not an unexpected error. You could use Popen.communicate() directly instead of check_output() to avoid raising an exception unnecessarily:
from subprocess import Popen, PIPE
p = Popen(['bitcoin', 'sendtoaddress', ..], stdout=PIPE)
output = p.communicate()[0]
if p.returncode != 0:
print("bitcoin failed %d %s" % (p.returncode, output))
Since Python 3.5, subprocess.run() supports check argument:
If check is true, and the process exits with a non-zero exit code, a CalledProcessError exception will be raised. Attributes of that exception hold the arguments, the exit code, and stdout and stderr if they were captured.
A simple example that will raise and print out CalledProcessError:
import subprocess
try:
subprocess.run("exit 1", shell=True, check=True, timeout=15, capture_output=True)
except subprocess.CalledProcessError as e:
print(e) # Output: Command 'exit 1' returned non-zero exit status 1.
There are good answers here, but in these answers, there has not been an answer that comes up with the text from the stack-trace output, which is the default behavior of an exception.
If you wish to use that formatted traceback information, you might wish to:
import traceback
try:
check_call( args )
except CalledProcessError:
tb = traceback.format_exc()
tb = tb.replace(passwd, "******")
print(tb)
exit(1)
As you might be able to tell, the above is useful in case you have a password in the check_call( args ) that you wish to prevent from displaying.
This did the trick for me. It captures all the stdout output from the subprocess(For python 3.8):
from subprocess import check_output, STDOUT
cmd = "Your Command goes here"
try:
cmd_stdout = check_output(cmd, stderr=STDOUT, shell=True).decode()
except Exception as e:
print(e.output.decode()) # print out the stdout messages up to the exception
print(e) # To print out the exception message
Based on the answer of #macetw I print the exception directly to stderr in a decorator.
Python 3
from functools import wraps
from sys import stderr
from traceback import format_exc
from typing import Callable, Collection, Any, Mapping
def force_error_output(func: Callable):
#wraps(func)
def forced_error_output(*args: Collection[Any], **kwargs: Mapping[str, Any]):
nonlocal func
try:
func(*args, **kwargs)
except Exception as exception:
stderr.write(format_exc())
stderr.write("\n")
stderr.flush()
raise exception
return forced_error_output
Python 2
from functools import wraps
from sys import stderr
from traceback import format_exc
def force_error_output(func):
#wraps(func)
def forced_error_output(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception as exception:
stderr.write(format_exc())
stderr.write("\n")
stderr.flush()
raise exception
return forced_error_output
Then in your worker just use the decorator
#force_error_output
def da_worker(arg1: int, arg2: str):
pass
I think most of previous answers are correct, in my case I needed to do this on Windows server and command was a Powershell, for that this worked really nicely for me:
try:
print("inpgoress")
cmd_exec="Get-Date"
print(cmd_aws)
subprocess.run(['powershell', '-Command', cmd_exec],shell=False,check=True,capture_output=True,text=True,encoding="utf-8")
except Exception as e:
print(e)
print("ERROR: something went wrong executing powershell command")
raise e
The subprocess invoked needs to be told to capture the output in the invoked program and raise the exception. It's simple to do it.
Firstly, Use
subprocess.run() instead of subprocess.call()
Let's assume u wanna python script called "Vijay.py".
For raising the exception, use the following;
subprocess.run("py vijay.py", check=True, capture_output=True, shell=True)
The above method then can be put in try and except block to immediately raise the error or can use sys.exit(1) :any non-zero exit is fine
try:
subprocess.call("py vijay.py", check=True, capture_output=True, shell=True)
except Exception as e:
print("Exception raised: ", e)
and body of vijay.py can be as follows;
vijay.py
try:
Your code is here...
except Exception as e:
sys.exit(1) // or can even use raise Exception("ur own exception to raise:)
enter code here
I have a python script 'b.py' which prints out time ever 5 sec.
while (1):
print "Start : %s" % time.ctime()
time.sleep( 5 )
print "End : %s" % time.ctime()
time.sleep( 5 )
And in my a.py, I call b.py by:
def run_b():
print "Calling run b"
try:
cmd = ["./b.py"]
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
print (">>>" + line.rstrip())
except OSError as e:
print >>sys.stderr, "fcs Execution failed:", e
return None
and later on, I kill 'b.py' by:
PS_PATH = "/usr/bin/ps -efW"
def kill_b(program):
try:
cmd = shlex.split(PS_PATH)
retval = subprocess.check_output(cmd).rstrip()
for line in retval.splitlines():
if program in line:
print "line =" + line
pid = line.split(None)[1]
os.kill(int(pid), signal.SIGKILL)
except OSError as e:
print >>sys.stderr, "kill_all Execution failed:", e
except subprocess.CalledProcessError as e:
print >>sys.stderr, "kill_all Execution failed:", e
run_b()
time.sleep(600)
kill_b("b.py")
I have 2 questions.
1. why I don't see any prints out from 'b.py' and when I do 'ps -efW' I don't see a process named 'b.py'?
2. Why when I kill a process like above, I see 'permission declined'?
I am running above script on cygwin under windows.
Thank you.
Why I don't see any prints out from 'b.py' and when I do 'ps -efW' I don't see a process named 'b.py'?
Change run_b() lines:
p = subprocess.Popen(cmd,
stdout=sys.stdout,
stderr=sys.stderr)
You will not see a process named "b.py" but something like "python b.py" which is little different. You should use pid instead of name to find it (in your code "p.pid" has the pid).
Why when I kill a process like above, I see 'permission declined'?
os.kill is supported under Windows only 2.7+ and acts a little bit different than posix version. However you can use "p.pid". Best way to kill a process in a cross platform way is:
if platform.system() == "Windows":
subprocess.Popen("taskkill /F /T /PID %i" % p.pid, shell=True)
else:
os.killpg(p.pid, signal.SIGKILL)
killpg works also on OS X and other Unixy operating systems.