diff --git a/docs/cmd.rst b/docs/cmd.rst index b88c5cf..91ecd08 100644 --- a/docs/cmd.rst +++ b/docs/cmd.rst @@ -1,7 +1,16 @@ Cmd -------- +--- -.. py:function:: pydu.cmd.run(cmd, wait=True, shell=True) +.. py:class:: pydu.cmd.TimeoutExpired(cmd, timeout, output=None, stderr=None) + + This exception is raised when the timeout expires while waiting for a + child process. + + Attributes: + cmd, output, stdout, stderr, timeout + + +.. py:function:: pydu.cmd.run(ccmd, wait=True, env=None, shell=False, timeout=None, timeinterval=1) Run cmd based on ``subprocess.Popen``. @@ -23,13 +32,28 @@ Cmd +.. py:function:: pydu.cmd.run(cmd, wait=True, shell=False, env=None, timeout=None, timeinterval=1) + + Run cmd with English character sets environment, so that the output will + be in English. + Parameters are same with ``run``. + + +.. py:function:: pydu.cmd.terminate(pid) + + Terminate process by given ``pid``. + + On Windows, using `kernel32.TerminateProcess` to kill. + On other platforms, using `os.kill` with `signal.SIGTERM` to kill. + + .. py:function:: pydu.cmd.cmdline_argv() Get command line argv of self python process. On Windows when using Python 2, ``cmdline_argv`` is implemented by using ``shell32.GetCommandLineArgvW`` to get sys.argv as a list of Unicode strings. - On other system or using Python 3, ``cmdline_argv`` is same to ``sys.argv``. + On other platforms or using Python 3, ``cmdline_argv`` is same to ``sys.argv``. >>> from pydu.cmd import cmdline_argv >>> cmdline_argv() diff --git a/pydu/cmd.py b/pydu/cmd.py index 05cca5a..5a85766 100644 --- a/pydu/cmd.py +++ b/pydu/cmd.py @@ -1,6 +1,7 @@ import os import sys import time +import signal import subprocess from subprocess import Popen, PIPE, STDOUT @@ -86,6 +87,23 @@ def run_with_en_env(cmd, wait=True, env=None, shell=False, timeout=None, timeint timeout=timeout, timeinterval=timeinterval) +def terminate(pid): + """ + Terminate process by given pid. + On Windows, using Kernel32.TerminateProcess to kill. + On Other platforms, using os.kill with signal.SIGTERM to kill. + """ + if WINDOWS: + # http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/ + import ctypes + PROCESS_TERMINATE = 1 + handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid) + ctypes.windll.kernel32.TerminateProcess(handle, -1) + ctypes.windll.kernel32.CloseHandle(handle) + else: + os.kill(pid, signal.SIGTERM) + + if PY2 and WINDOWS: # enable passing unicode arguments from command line in Python 2.x # https://stackoverflow.com/questions/846850/read-unicode-characters diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 41f5364..e088792 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,9 +1,9 @@ import sys import pytest -from pydu.platform import WINDOWS +import time from pydu.compat import string_types from pydu.string import safeunicode -from pydu.cmd import TimeoutExpired, run, run_with_en_env, cmdline_argv +from pydu.cmd import TimeoutExpired, run, run_with_en_env, terminate, cmdline_argv def test_run(): @@ -32,6 +32,14 @@ def test_run_with_en_env(): assert output.decode('ascii') +def test_terminate(): + p = run('{} -c "import time; time.sleep(1)"'.format(sys.executable), + wait=False, shell=True) + terminate(p.pid) + time.sleep(0.1) + assert p.poll() is not None + + def test_cmdline_argv(): argv = cmdline_argv() for s in argv[1:]: