📄 process.py
字号:
assert errCode == 0,\
"Why is 'errCode' from ReadFile non-zero? %r" % errCode
if not text:
# Empty text signifies that the pipe has been closed on
# the parent's end.
log.info("[%s] _FileWrapper.read: observed close of parent",
id(self))
# Signal the child so it knows to stop listening.
self.close()
return ""
else:
log.info("[%s] _FileWrapper.read: read %d bytes from pipe: %r",
id(self), len(text), text)
return text
def read(self, nBytes=-1):
# nBytes <= 0 means "read everything"
# Note that we are changing the "read everything" cue to
# include 0, because actually doing
# win32file.ReadFile(<handle>, 0) results in every subsequent
# read returning 0, i.e. it shuts down the pipe.
if self._descriptor is not None:
if nBytes <= 0:
text, self._lineBuf = self._lineBuf, ""
while 1:
t = os.read(self._descriptor, 4092)
if not t:
break
else:
text += t
else:
if len(self._lineBuf) >= nBytes:
text, self._lineBuf =\
self._lineBuf[:nBytes], self._lineBuf[nBytes:]
else:
nBytesToGo = nBytes - len(self._lineBuf)
text = self._lineBuf + os.read(self._descriptor,
nBytesToGo)
self._lineBuf = ""
return text
elif self._handle is not None:
if nBytes <= 0:
text, self._lineBuf = self._lineBuf, ""
while 1:
t = self._win32Read(4092)
if not t:
break
else:
text += t
else:
if len(self._lineBuf) >= nBytes:
text, self._lineBuf =\
self._lineBuf[:nBytes], self._lineBuf[nBytes:]
else:
nBytesToGo = nBytes - len(self._lineBuf)
text, self._lineBuf =\
self._lineBuf + self._win32Read(nBytesToGo), ""
return text
elif self._file is not None:
return self._file.read(nBytes)
else:
raise "FileHandle.read: no handle to read with"
def readline(self):
if self._descriptor is not None or self._handle is not None:
while 1:
#XXX This is not portable to the Mac.
idx = self._lineBuf.find('\n')
if idx != -1:
line, self._lineBuf =\
self._lineBuf[:idx+1], self._lineBuf[idx+1:]
break
else:
lengthBefore = len(self._lineBuf)
t = self.read(4092)
if len(t) <= lengthBefore: # no new data was read
line, self._lineBuf = self._lineBuf, ""
break
else:
self._lineBuf += t
return line
elif self._file is not None:
return self._file.readline()
else:
raise "FileHandle.readline: no handle to read with"
def readlines(self):
if self._descriptor is not None or self._handle is not None:
lines = []
while 1:
line = self.readline()
if line:
lines.append(line)
else:
break
return lines
elif self._file is not None:
return self._file.readlines()
else:
raise "FileHandle.readline: no handle to read with"
def write(self, text):
if self._descriptor is not None:
os.write(self._descriptor, text)
elif self._handle is not None:
try:
errCode, nBytesWritten = win32file.WriteFile(self._handle, text)
except pywintypes.error, ex:
# Ingore errors like "The pipe is being closed.", for
# now.
log.info("[%s] _FileWrapper.write: error writing to pipe, "\
"ignored", id(self))
return
assert errCode == 0,\
"Why is 'errCode' from WriteFile non-zero? %r" % errCode
if not nBytesWritten:
# No bytes written signifies that the pipe has been
# closed on the child's end.
log.info("[%s] _FileWrapper.write: observed close of pipe",
id(self))
return
else:
log.info("[%s] _FileWrapper.write: wrote %d bytes to pipe: %r",
id(self), len(text), text)
elif self._file is not None:
self._file.write(text)
else:
raise "FileHandle.write: nothing to write with"
def close(self):
"""Close all associated file objects and handles."""
log.debug("[%s] _FileWrapper.close()", id(self))
if not self._closed:
self._closed = 1
if self._file is not None:
log.debug("[%s] _FileWrapper.close: close file", id(self))
self._file.close()
log.debug("[%s] _FileWrapper.close: done file close", id(self))
if self._descriptor is not None:
try:
os.close(self._descriptor)
except OSError, ex:
if ex.errno == 9:
# Ignore: OSError: [Errno 9] Bad file descriptor
# XXX *Should* we be ignoring this? It appears very
# *in*frequently in test_wait.py.
log.debug("[%s] _FileWrapper.close: closing "\
"descriptor raised OSError", id(self))
else:
raise
if self._handle is not None:
log.debug("[%s] _FileWrapper.close: close handle", id(self))
try:
win32api.CloseHandle(self._handle)
except win32api.error:
log.debug("[%s] _FileWrapper.close: closing handle raised",
id(self))
pass
log.debug("[%s] _FileWrapper.close: done closing handle",
id(self))
def __repr__(self):
return "<_FileWrapper: file:%r fd:%r os_handle:%r>"\
% (self._file, self._descriptor, self._handle)
class _CountingCloser:
"""Call .close() on the given object after own .close() is called
the precribed number of times.
"""
def __init__(self, objectsToClose, count):
"""
"objectsToClose" is a list of object on which to call .close().
"count" is the number of times this object's .close() method
must be called before .close() is called on the given objects.
"""
self.objectsToClose = objectsToClose
self.count = count
if self.count <= 0:
raise ProcessError("illegal 'count' value: %s" % self.count)
def close(self):
self.count -= 1
log.debug("[%d] _CountingCloser.close(): count=%d", id(self),
self.count)
if self.count == 0:
for objectToClose in self.objectsToClose:
objectToClose.close()
#---- public interface
class Process:
"""Create a process.
One can optionally specify the starting working directory, the
process environment, and std handles to have the child process
inherit (all defaults are the parent's current settings). 'wait' and
'kill' method allow for control of the child's termination.
"""
# TODO:
# - Rename this or merge it with ProcessOpen somehow.
#
if sys.platform.startswith("win"):
# .wait() argument constants
INFINITE = win32event.INFINITE
# .wait() return error codes
WAIT_FAILED = win32event.WAIT_FAILED
WAIT_TIMEOUT = win32event.WAIT_TIMEOUT
# creation "flags" constants
# XXX Should drop these and just document usage of
# win32process.CREATE_* constants on windows.
CREATE_NEW_CONSOLE = win32process.CREATE_NEW_CONSOLE
else:
# .wait() argument constants
INFINITE = 0
# .wait() return error codes
WAIT_TIMEOUT = 258
WAIT_FAILED = -1
# creation "flags" constants
CREATE_NEW_CONSOLE = 0x10 # same as win32process.CREATE_NEW_CONSOLE
def __init__(self, cmd, cwd=None, env=None, flags=0):
"""Create a child process.
"cmd" is a command string or argument vector to spawn.
"cwd" is a working directory in which to start the child process.
"env" is an environment dictionary for the child.
"flags" are system-specific process creation flags. On Windows
this can be a bitwise-OR of any of the win32process.CREATE_*
constants (Note: win32process.CREATE_NEW_PROCESS_GROUP is always
OR'd in). On Unix, this is currently ignored.
"""
log.info("Process.__init__(cmd=%r, cwd=%r, env=%r, flags=%r)",
cmd, cwd, env, flags)
self._cmd = cmd
if not self._cmd:
raise ProcessError("You must specify a command.")
self._cwd = cwd
self._env = env
self._flags = flags
if sys.platform.startswith("win"):
self._flags |= win32process.CREATE_NEW_PROCESS_GROUP
if sys.platform.startswith("win"):
self._startOnWindows()
else:
self.__retvalCache = None
self._startOnUnix()
def _runChildOnUnix(self):
#XXX Errors running the child do *not* get communicated back.
#XXX Perhaps we should *always* prefix with '/bin/sh -c'? There is a
# disparity btwn how this works on Linux and Windows.
if isinstance(self._cmd, types.StringTypes):
# This is easier than trying to reproduce shell interpretation to
# separate the arguments.
cmd = ['/bin/sh', '-c', self._cmd]
else:
cmd = self._cmd
# Close all file descriptors (except std*) inherited from the parent.
MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
for i in range(3, MAXFD):
try:
os.close(i)
except OSError:
pass
try:
if self._env:
os.execvpe(cmd[0], cmd, self._env)
else:
os.execvp(cmd[0], cmd)
finally:
os._exit(1) # Should never get here.
def _forkAndExecChildOnUnix(self):
"""Fork and start the child process.
Sets self._pid as a side effect.
"""
pid = os.fork()
if pid == 0: # child
self._runChildOnUnix()
# parent
self._pid = pid
def _startOnUnix(self):
if self._cwd:
oldDir = os.getcwd()
try:
os.chdir(self._cwd)
except OSError, ex:
raise ProcessError(msg=str(ex), errno=ex.errno)
self._forkAndExecChildOnUnix()
# parent
if self._cwd:
os.chdir(oldDir)
def _startOnWindows(self):
if type(self._cmd) in (types.ListType, types.TupleType):
# And arg vector was passed in.
cmd = _joinArgv(self._cmd)
else:
cmd = self._cmd
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -