make py lib a self-contained directory again
- move and merge _py/ bits back to py/ - fixes all around --HG-- branch : trunk
This commit is contained in:
1
py/impl/process/__init__.py
Normal file
1
py/impl/process/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
""" high-level sub-process handling """
|
||||
44
py/impl/process/cmdexec.py
Normal file
44
py/impl/process/cmdexec.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
import os, sys
|
||||
import subprocess
|
||||
import py
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
def cmdexec(cmd):
|
||||
""" return output of executing 'cmd' in a separate process.
|
||||
|
||||
raise cmdexec.ExecutionFailed exeception if the command failed.
|
||||
the exception will provide an 'err' attribute containing
|
||||
the error-output from the command.
|
||||
"""
|
||||
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = process.communicate()
|
||||
out = py.builtin._totext(out, sys.getdefaultencoding())
|
||||
err = py.builtin._totext(err, sys.getdefaultencoding())
|
||||
status = process.poll()
|
||||
if status:
|
||||
raise ExecutionFailed(status, status, cmd, out, err)
|
||||
return out
|
||||
|
||||
class ExecutionFailed(py.error.Error):
|
||||
def __init__(self, status, systemstatus, cmd, out, err):
|
||||
Exception.__init__(self)
|
||||
self.status = status
|
||||
self.systemstatus = systemstatus
|
||||
self.cmd = cmd
|
||||
self.err = err
|
||||
self.out = out
|
||||
|
||||
def __str__(self):
|
||||
return "ExecutionFailed: %d %s\n%s" %(self.status, self.cmd, self.err)
|
||||
|
||||
# export the exception under the name 'py.process.cmdexec.Error'
|
||||
cmdexec.Error = ExecutionFailed
|
||||
try:
|
||||
ExecutionFailed.__module__ = 'py.process.cmdexec'
|
||||
ExecutionFailed.__name__ = 'Error'
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
108
py/impl/process/forkedfunc.py
Normal file
108
py/impl/process/forkedfunc.py
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
"""
|
||||
ForkedFunc provides a way to run a function in a forked process
|
||||
and get at its return value, stdout and stderr output as well
|
||||
as signals and exitstatusus.
|
||||
|
||||
XXX see if tempdir handling is sane
|
||||
"""
|
||||
|
||||
import py
|
||||
import os
|
||||
import sys
|
||||
import marshal
|
||||
|
||||
class ForkedFunc(object):
|
||||
EXITSTATUS_EXCEPTION = 3
|
||||
def __init__(self, fun, args=None, kwargs=None, nice_level=0):
|
||||
if args is None:
|
||||
args = []
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
self.fun = fun
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.tempdir = tempdir = py.path.local.mkdtemp()
|
||||
self.RETVAL = tempdir.ensure('retval')
|
||||
self.STDOUT = tempdir.ensure('stdout')
|
||||
self.STDERR = tempdir.ensure('stderr')
|
||||
|
||||
pid = os.fork()
|
||||
if pid: # in parent process
|
||||
self.pid = pid
|
||||
else: # in child process
|
||||
self._child(nice_level)
|
||||
|
||||
def _child(self, nice_level):
|
||||
# right now we need to call a function, but first we need to
|
||||
# map all IO that might happen
|
||||
# make sure sys.stdout points to file descriptor one
|
||||
sys.stdout = stdout = self.STDOUT.open('w')
|
||||
sys.stdout.flush()
|
||||
fdstdout = stdout.fileno()
|
||||
if fdstdout != 1:
|
||||
os.dup2(fdstdout, 1)
|
||||
sys.stderr = stderr = self.STDERR.open('w')
|
||||
fdstderr = stderr.fileno()
|
||||
if fdstderr != 2:
|
||||
os.dup2(fdstderr, 2)
|
||||
retvalf = self.RETVAL.open("wb")
|
||||
EXITSTATUS = 0
|
||||
try:
|
||||
if nice_level:
|
||||
os.nice(nice_level)
|
||||
try:
|
||||
retval = self.fun(*self.args, **self.kwargs)
|
||||
retvalf.write(marshal.dumps(retval))
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
stderr.write(excinfo.exconly())
|
||||
EXITSTATUS = self.EXITSTATUS_EXCEPTION
|
||||
finally:
|
||||
stdout.close()
|
||||
stderr.close()
|
||||
retvalf.close()
|
||||
os.close(1)
|
||||
os.close(2)
|
||||
os._exit(EXITSTATUS)
|
||||
|
||||
def waitfinish(self, waiter=os.waitpid):
|
||||
pid, systemstatus = waiter(self.pid, 0)
|
||||
if systemstatus:
|
||||
if os.WIFSIGNALED(systemstatus):
|
||||
exitstatus = os.WTERMSIG(systemstatus) + 128
|
||||
else:
|
||||
exitstatus = os.WEXITSTATUS(systemstatus)
|
||||
#raise ExecutionFailed(status, systemstatus, cmd,
|
||||
# ''.join(out), ''.join(err))
|
||||
else:
|
||||
exitstatus = 0
|
||||
signal = systemstatus & 0x7f
|
||||
if not exitstatus and not signal:
|
||||
retval = self.RETVAL.open('rb')
|
||||
try:
|
||||
retval_data = retval.read()
|
||||
finally:
|
||||
retval.close()
|
||||
retval = marshal.loads(retval_data)
|
||||
else:
|
||||
retval = None
|
||||
stdout = self.STDOUT.read()
|
||||
stderr = self.STDERR.read()
|
||||
self._removetemp()
|
||||
return Result(exitstatus, signal, retval, stdout, stderr)
|
||||
|
||||
def _removetemp(self):
|
||||
if self.tempdir.check():
|
||||
self.tempdir.remove()
|
||||
|
||||
def __del__(self):
|
||||
self._removetemp()
|
||||
|
||||
class Result(object):
|
||||
def __init__(self, exitstatus, signal, retval, stdout, stderr):
|
||||
self.exitstatus = exitstatus
|
||||
self.signal = signal
|
||||
self.retval = retval
|
||||
self.out = stdout
|
||||
self.err = stderr
|
||||
23
py/impl/process/killproc.py
Normal file
23
py/impl/process/killproc.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import py
|
||||
import os, sys
|
||||
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import ctypes
|
||||
except ImportError:
|
||||
def dokill(pid):
|
||||
py.process.cmdexec("taskkill /F /PID %d" %(pid,))
|
||||
else:
|
||||
def dokill(pid):
|
||||
PROCESS_TERMINATE = 1
|
||||
handle = ctypes.windll.kernel32.OpenProcess(
|
||||
PROCESS_TERMINATE, False, pid)
|
||||
ctypes.windll.kernel32.TerminateProcess(handle, -1)
|
||||
ctypes.windll.kernel32.CloseHandle(handle)
|
||||
else:
|
||||
def dokill(pid):
|
||||
os.kill(pid, 15)
|
||||
|
||||
def kill(pid):
|
||||
""" kill process by id. """
|
||||
dokill(pid)
|
||||
Reference in New Issue
Block a user