Skip to content

Commit

Permalink
Move Command to subprocess
Browse files Browse the repository at this point in the history
  • Loading branch information
gmatteo committed Oct 26, 2014
1 parent 7895b18 commit b872885
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 98 deletions.
8 changes: 8 additions & 0 deletions docs/monty.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ monty.string module
:undoc-members:
:show-inheritance:

monty.subprocess module
-------------------

.. automodule:: monty.subprocess
:members:
:undoc-members:
:show-inheritance:

monty.tempfile module
---------------------

Expand Down
82 changes: 0 additions & 82 deletions monty/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,85 +125,3 @@ def decompress_dir(path):
for f in files:
decompress_file(os.path.join(parent, f))


class Command(object):
"""
Enables to run subprocess commands in a different thread with TIMEOUT option.
Based on jcollado's solution:
http://stackoverflow.com/questions/1191374/subprocess-with-timeout/4825933#4825933
and
https://gist.github.com/kirpit/1306188
.. attribute:: retcode
Return code of the subprocess
.. attribute:: killed
True if subprocess has been killed due to the timeout
.. attribute:: output
stdout of the subprocess
.. attribute:: error
stderr of the subprocess
Example:
com = Command("sleep 1").run(timeout=2)
print(com.retcode, com.killed, com.output, com.output)
"""
def __init__(self, command):
from .string import is_string
if is_string(command):
import shlex
command = shlex.split(command)

self.command = command
self.process = None
self.retcode = None
self.output, self.error = '', ''
self.killed = False

def __str__(self):
return "command: %s, retcode: %s" % (str(self.command), str(self.retcode))

def run(self, timeout=None, **kwargs):
"""
Run a command in a separated thread and wait timeout seconds.
kwargs are keyword arguments passed to Popen.
Return: self
"""
from subprocess import Popen, PIPE
def target(**kwargs):
try:
#print('Thread started')
self.process = Popen(self.command, **kwargs)
self.output, self.error = self.process.communicate()
self.retcode = self.process.returncode
#print('Thread stopped')
except:
import traceback
self.error = traceback.format_exc()
self.retcode = -1

# default stdout and stderr
if 'stdout' not in kwargs: kwargs['stdout'] = PIPE
if 'stderr' not in kwargs: kwargs['stderr'] = PIPE

# thread
import threading
thread = threading.Thread(target=target, kwargs=kwargs)
thread.start()
thread.join(timeout)

if thread.is_alive():
#print("Terminating process")
self.process.terminate()
self.killed = True
thread.join()

return self
92 changes: 92 additions & 0 deletions monty/subprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# coding: utf-8
from __future__ import absolute_import, print_function, division, unicode_literals

__author__ = 'Matteo Giantomass'
__copyright__ = "Copyright 2014, The Materials Virtual Lab"
__version__ = '0.1'
__maintainer__ = 'Matteo Giantomassi'
__email__ = 'gmatteo@gmail.com'
__date__ = '10/26/14'


class Command(object):
"""
Enables to run subprocess commands in a different thread with TIMEOUT option.
Based on jcollado's solution:
http://stackoverflow.com/questions/1191374/subprocess-with-timeout/4825933#4825933
and
https://gist.github.com/kirpit/1306188
.. attribute:: retcode
Return code of the subprocess
.. attribute:: killed
True if subprocess has been killed due to the timeout
.. attribute:: output
stdout of the subprocess
.. attribute:: error
stderr of the subprocess
Example:
com = Command("sleep 1").run(timeout=2)
print(com.retcode, com.killed, com.output, com.output)
"""
def __init__(self, command):
from .string import is_string
if is_string(command):
import shlex
command = shlex.split(command)

self.command = command
self.process = None
self.retcode = None
self.output, self.error = '', ''
self.killed = False

def __str__(self):
return "command: %s, retcode: %s" % (str(self.command), str(self.retcode))

def run(self, timeout=None, **kwargs):
"""
Run a command in a separated thread and wait timeout seconds.
kwargs are keyword arguments passed to Popen.
Return: self
"""
from subprocess import Popen, PIPE
def target(**kwargs):
try:
#print('Thread started')
self.process = Popen(self.command, **kwargs)
self.output, self.error = self.process.communicate()
self.retcode = self.process.returncode
#print('Thread stopped')
except:
import traceback
self.error = traceback.format_exc()
self.retcode = -1

# default stdout and stderr
if 'stdout' not in kwargs: kwargs['stdout'] = PIPE
if 'stderr' not in kwargs: kwargs['stderr'] = PIPE

# thread
import threading
thread = threading.Thread(target=target, kwargs=kwargs)
thread.start()
thread.join(timeout)

if thread.is_alive():
#print("Terminating process")
self.process.terminate()
self.killed = True
thread.join()

return self
17 changes: 1 addition & 16 deletions tests/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from io import open

from monty.shutil import copy_r, compress_file, decompress_file, \
compress_dir, decompress_dir, Command
compress_dir, decompress_dir

test_dir = os.path.join(os.path.dirname(__file__), 'test_files')

Expand Down Expand Up @@ -82,20 +82,5 @@ def tearDown(self):
os.remove(os.path.join(test_dir, "tempfile"))


class CommandTest(unittest.TestCase):
def test_command(self):
"""Test Command class"""
sleep05 = Command("sleep 0.5")

sleep05.run(timeout=1)
print(sleep05)
self.assertEqual(sleep05.retcode, 0)
self.assertFalse(sleep05.killed)

sleep05.run(timeout=0.1)
self.assertNotEqual(sleep05.retcode, 0)
self.assertTrue(sleep05.killed)


if __name__ == "__main__":
unittest.main()
21 changes: 21 additions & 0 deletions tests/test_subprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest

from monty.subprocess import Command

class CommandTest(unittest.TestCase):
def test_command(self):
"""Test Command class"""
sleep05 = Command("sleep 0.5")

sleep05.run(timeout=1)
print(sleep05)
self.assertEqual(sleep05.retcode, 0)
self.assertFalse(sleep05.killed)

sleep05.run(timeout=0.1)
self.assertNotEqual(sleep05.retcode, 0)
self.assertTrue(sleep05.killed)


if __name__ == "__main__":
unittest.main()

0 comments on commit b872885

Please sign in to comment.