Adding timeout in subprocess.check_output - python

I am developing a small tool in Python 2.7 and using subprocess module. I am using this module to run commands on remote devices using its check_output function. There might be a situation in which the remote device is not functioning and therefore I am getting the following response:
Timeout: No Response from 10.xxx.xxx.xxx
Following is my code:
try:
x=subprocess.check_output(command, shell=True)
except Exception:
print ("Some issues in fetching details")
exit()
else:
print (x)
I want to put timeout into this function so that if after a certain amount of time, no response is recieved, my code goes in the Exception part and prints the given message. I tried using timeout argument in the check_output command but after running my script with timeout argument, it is immediately printing the message given in the Exception part.
What I tried:
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except Exception:
print ("Some issues in fetching details")
exit()
else:
print (x)

My guess is that you are running your code in Python 2.
If that is the case, subprocess.check_output() does not accept a timeout parameter, and the function will fail immediately with:
TypeError: __init__() got an unexpected keyword argument 'timeout'
But, because you are catching all exceptions and printing a generic message, you don't see the actual exception, and you assume that the command is timing out immediately.
One way to fix this problem is to run your code in Python 3.
Whether you are running Python 2 or 3, I recommend that you do not catch all exceptions, or that you at least print the value of the exception so that you can see the actual cause, e.g.
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except subprocess.TimeoutExpired as exc:
print("Command timed out: {}".format(exc))
exit()
else:
print (x)
which explicitly checks for a timeout exception. All other exceptions are propagated as usual and so are not masked by your "catch all" code. Or,
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except Exception as exc:
print("Command failed: {}".format(exc))
exit()
else:
print (x)
but the former is preferred.
Edit
OP can't use Python 3. If you are using Linux then you could use the timeout command, e.g.
x = subprocess.check_output('timeout 5 {}'.format(command), shell=True)
On timeout this will raise an exception with a particular exit status value of 124:
subprocess.CalledProcessError: Command 'timeout 5 sleep 10' returned non-zero exit status 124
BTW you shouldn't use the shell=True option as there are security implications as mentioned in the documentation. Instead you should pass a list of strings to check_output() like this:
from shlex import shlex
command = shlex('timeout 5 {}'.format(command))
try:
x = subprocess.check_output(command)
except subprocess.CalledProcessError as exc:
if exc.returncode == 124:
print "Command timed out"
else:
raise
If you are using another OS (or you don't want to use timeout) then you can run your subprocess in a separate thread and have your main thread time it out if required. See this other question, Using module 'subprocess' with timeout, for details about how to do that.

Python 2.7 does not support timeout parameter. You can instead use EasyProcess. This is a layer on top of subprocess module and pretty easy to use.

Related

Python Subprocess Catch Process Error If There Is Any and Kill After 5 Seconds

I would like to create subprocess which runs a node.js program (node app.js), catch if there is any process error and kill the process after 5 seconds whether there is any process error or not.
First, I tried check_call method:
try:
server = subprocess.check_call("node app.js")
time.sleep(5)
server.kill()
server.terminate()
except subprocess.CalledProcessError as e:
print(e)
But problem is that when I start a subprocess that runs a node.js program with check_call method, if there is no process error, there is no way to kill the process except keyboard interrupt.
I also tried subprocess.Popen:
try:
server = subprocess.Popen("node app.js" , stdout=subprocess.PIPE)
time.sleep(5)
server.kill()
server.terminate()
except subprocess.CalledProcessError as e:
print(e)
This time, I was able to kill the process after 5 seconds, but I could not catch the process error. When I tried it with server.communicate() I faced same problem as with check_call.
What should I do to both catch a process error if there is any and kill subprocess after 5 seconds?

How do I get exit code after exec_command?

I have a program that runs from my local computer and connects via SSH (paramiko package) to a Linux computer.
I use the following functions to send a command and get an exit_code to make sure it's done.
For some reason, sometimes an exit code is returned, whereas sometimes the code enters an endless loop.
Does anyone know why this happens and how to make it stable?
def check_on_command(self, stdin, stdout, stderr):
if stdout is None:
raise Exception("Tried to check command before it was ready")
if not stdout.channel.exit_status_ready():
return None
else:
return stdout.channel.recv_exit_status()
def run_command(self, command):
(stdin, stdout, stderr) = self.client.exec_command(command)
logger.info(f"Excute command: {command}")
while self.check_on_command(stdin, stdout, stderr) is None:
time.sleep(5)
logger.info(f'Finish running, exit code: {stdout.channel.recv_exit_status()}')
In case you're using Python version >= 3.6, I advise working with an asynchronous library, that provides await capabilities for optimized run times and more manageable simple code.
For example, you can use asyncssh library that comes with python and does the job as requested. In general writing async code that uses sleeps to wait for a task to be executed should be replaced like so.
import asyncio, asyncssh, sys
async def run_client():
async with asyncssh.connect('localhost') as conn:
result = await conn.run('ls abc')
if result.exit_status == 0:
print(result.stdout, end='')
else:
print(result.stderr, end='', file=sys.stderr)
print('Program exited with status %d' % result.exit_status,
file=sys.stderr)
try:
asyncio.get_event_loop().run_until_complete(run_client())
except (OSError, asyncssh.Error) as exc:
sys.exit('SSH connection failed: ' + str(exc))
You can find further documentation here: asyncssh

Python3 subprocess.check_output catch exception without printing

I'm working on using subprocess.check_output to call ping command and get some output. The script will catch the wrong domain name. However, the output of the ping command will always print out on its own. When there's no exception, the output will not print out on its own.
hostname = "somewrongdomain.com" # This domain will not connect
try:
response = (subprocess.check_output(["ping", "-c 1", self.hostname]).decode("utf-8")
print(response)
except subprocess.TimeoutExpired as e:
ping_rating = 0
except subprocess.CalledProcessError as e:
ping_rating = 0
#do something else
The output I'm getting:
$ python3 test.py
ping: sendto: No route to host
Is there any way to NOT print the output(ping: sendto: No route to host) when the exception is caught?
As written in the documentation subprocess.check_output only captures STDOUT.
To also capture standard error in the result, use stderr=subprocess.STDOUT:
>>> subprocess.check_output(
... "ls non_existent_file; exit 0",
... stderr=subprocess.STDOUT,
... shell=True)
'ls: non_existent_file: No such file or directory\n'

How to log to Kubernetes Container Log from Python process

With Kubernetes Container running a Python script:
import time
while True:
try:
for i in range(10):
if i==0:
raise Exception('Exception occurred!')
except:
pass
time.sleep(1)
I would like to pass the Exception's message 'Exception occurred!' down to the Container so this error message could be seen with:
kubectl describe pod pod_id
Would it be possible?
Anything you print() will be visible in kubectl logs. (You may need to set an environment variable PYTHONUNBUFFERED=1 in your pod spec.)
Your code as you've written it will never print anything. The construct
try:
...
except:
pass
silently ignores any and all exceptions out of the try block. The bare except: even captures some system-level exceptions like SystemExit or KeyboardInterrupt; this is almost always wrong. Often you want your except blocks to be as tightly scoped as you can, and the Python tutorial on user-defined exceptions is a helpful pattern.
(The exception to this, particularly in a Kubernetes context, is that you will often want a very broad exception handler to do something like return an HTTP 500 error to a network request, rather than crashing the application.)
A better example might look like:
import time
class OneException(Exception):
pass
def iteration():
for i in range(10):
try:
if i == 1:
raise OneException("it is one")
print(i, math.sqrt(i), math.sqrt(-i))
# will work when i==0 but fail when i==2
except OneException as e:
print(i, repr(e))
# and proceed to the next iteration
if __name__ == '__main__':
while True:
# The top-level loop. We want a very broad catch here.
try:
iteration()
except Exception as e:
print('iteration failed', repr(e))
time.sleep(1)

Python: except EOFError: ... doesn't work

I have a try/except block that sends a message and waits for confirmation from client. If the client terminates, pickle raises an EOFError, but the code below does not catch the error and execute the graceful shut down. It instead prints stack trace. I assume it has to do with the line "except socket.error, EOFError:" - am I using the wrong syntax to handle both socket.error and EOFError there?
try:
msgs = [1]
self.sock.send(pickle.dumps(msgs))
rdy = pickle.loads(self.sock.recv(2097152))
except socket.error, EOFError:
print 'log socketmanager closing'
self.terminate()
break
In Python 2.x, the form except a, b catches an exception of type a and assign it to a variable called b. In your case this would result in EOFError being ignored. Try this instead:
...
except (socket.error, EOFError):
...
Edit: to elaborate, the new syntax in Python 3.0, and available, though not required, in 2.6+, for capturing the value of an exception is except a as b.
break is causing the error, it can only be used inside a for loop or a try/finally block, not try/except, see docs and more.

Categories

Resources