def test_callProcessExitedMissing(self): """ L{BaseProcess._callProcessExited} emits a L{DeprecationWarning} if the object referred to by its C{proto} attribute has no C{processExited} method. """ class FakeProto: pass reason = object() process = BaseProcess(FakeProto()) self.addCleanup(setWarningMethod, getWarningMethod()) warnings = [] def collect(message, category, stacklevel): warnings.append((message, category, stacklevel)) setWarningMethod(collect) process._callProcessExited(reason) [(message, category, stacklevel)] = warnings self.assertEqual( message, "Since Twisted 8.2, IProcessProtocol.processExited is required. " "%s.%s must implement it." % ( FakeProto.__module__, FakeProto.__name__)) self.assertIdentical(category, DeprecationWarning) # The stacklevel doesn't really make sense for this kind of # deprecation. Requiring it to be 0 will at least avoid pointing to # any part of Twisted or a random part of the application's code, which # I think would be more misleading than having it point inside the # warning system itself. -exarkun self.assertEqual(stacklevel, 0)
def maybeCallProcessEnded(self): if self.closedNotifies == 3 and self.lostProcess: win32file.CloseHandle(self.hProcess) win32file.CloseHandle(self.hThread) self.hProcess = None self.hThread = None BaseProcess.maybeCallProcessEnded(self)
def __init__(self, protocol): BaseProcess.__init__(self, protocol) if not signal.getsignal(signal.SIGCHLD): warnings.warn( error.PotentialZombieWarning.MESSAGE, error.PotentialZombieWarning, stacklevel=4)
def test_callProcessExited(self): """ L{BaseProcess._callProcessExited} calls the C{processExited} method of its C{proto} attribute and passes it a L{Failure} wrapping the given exception. """ class FakeProto: reason = None def processExited(self, reason): self.reason = reason reason = RuntimeError("fake reason") process = BaseProcess(FakeProto()) process._callProcessExited(reason) process.proto.reason.trap(RuntimeError) self.assertIdentical(reason, process.proto.reason.value)
def __init__(self, reactor, protocol, command, args, environment, path): _pollingfile._PollingTimer.__init__(self, reactor) BaseProcess.__init__(self, protocol) # security attributes for pipes sAttrs = win32security.SECURITY_ATTRIBUTES() sAttrs.bInheritHandle = 1 # create the pipes which will connect to the secondary process self.hStdoutR, hStdoutW = win32pipe.CreatePipe(sAttrs, 0) self.hStderrR, hStderrW = win32pipe.CreatePipe(sAttrs, 0) hStdinR, self.hStdinW = win32pipe.CreatePipe(sAttrs, 0) win32pipe.SetNamedPipeHandleState(self.hStdinW, win32pipe.PIPE_NOWAIT, None, None) # set the info structure for the new process. StartupInfo = win32process.STARTUPINFO() StartupInfo.hStdOutput = hStdoutW StartupInfo.hStdError = hStderrW StartupInfo.hStdInput = hStdinR StartupInfo.dwFlags = win32process.STARTF_USESTDHANDLES # Create new handles whose inheritance property is false currentPid = win32api.GetCurrentProcess() tmp = win32api.DuplicateHandle(currentPid, self.hStdoutR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdoutR) self.hStdoutR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStderrR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStderrR) self.hStderrR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStdinW, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdinW) self.hStdinW = tmp # Add the specified environment to the current environment - this is # necessary because certain operations are only supported on Windows # if certain environment variables are present. env = os.environ.copy() env.update(environment or {}) cmdline = quoteArguments(args) # TODO: error detection here. def doCreate(): self.hProcess, self.hThread, self.pid, dwTid = win32process.CreateProcess( command, cmdline, None, None, 1, 0, env, path, StartupInfo) try: doCreate() except pywintypes.error, pwte: if not _invalidWin32App(pwte): # This behavior isn't _really_ documented, but let's make it # consistent with the behavior that is documented. raise OSError(pwte) else: # look for a shebang line. Insert the original 'command' # (actually a script) into the new arguments list. sheb = _findShebang(command) if sheb is None: raise OSError( "%r is neither a Windows executable, " "nor a script with a shebang line" % command) else: args = list(args) args.insert(0, command) cmdline = quoteArguments(args) origcmd = command command = sheb try: # Let's try again. doCreate() except pywintypes.error, pwte2: # d'oh, failed again! if _invalidWin32App(pwte2): raise OSError( "%r has an invalid shebang line: " "%r is not a valid executable" % ( origcmd, sheb)) raise OSError(pwte2)
def __init__(self, reactor, protocol, command, args, environment, path): """ Create a new child process. """ _pollingfile._PollingTimer.__init__(self, reactor) BaseProcess.__init__(self, protocol) # security attributes for pipes sAttrs = win32security.SECURITY_ATTRIBUTES() sAttrs.bInheritHandle = 1 # create the pipes which will connect to the secondary process self.hStdoutR, hStdoutW = win32pipe.CreatePipe(sAttrs, 0) self.hStderrR, hStderrW = win32pipe.CreatePipe(sAttrs, 0) hStdinR, self.hStdinW = win32pipe.CreatePipe(sAttrs, 0) win32pipe.SetNamedPipeHandleState(self.hStdinW, win32pipe.PIPE_NOWAIT, None, None) # set the info structure for the new process. StartupInfo = win32process.STARTUPINFO() StartupInfo.hStdOutput = hStdoutW StartupInfo.hStdError = hStderrW StartupInfo.hStdInput = hStdinR StartupInfo.dwFlags = win32process.STARTF_USESTDHANDLES # Create new handles whose inheritance property is false currentPid = win32api.GetCurrentProcess() tmp = win32api.DuplicateHandle(currentPid, self.hStdoutR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdoutR) self.hStdoutR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStderrR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStderrR) self.hStderrR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStdinW, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdinW) self.hStdinW = tmp # Add the specified environment to the current environment - this is # necessary because certain operations are only supported on Windows # if certain environment variables are present. env = os.environ.copy() env.update(environment or {}) newenv = {} for key, value in items(env): key = _fsdecode(key) value = _fsdecode(value) newenv[key] = value env = newenv # Make sure all the arguments are Unicode. args = [_fsdecode(x) for x in args] cmdline = quoteArguments(args) # The command, too, needs to be Unicode, if it is a value. command = _fsdecode(command) if command else command path = _fsdecode(path) if path else path # TODO: error detection here. See #2787 and #4184. def doCreate(): flags = win32con.CREATE_NO_WINDOW self.hProcess, self.hThread, self.pid, dwTid = win32process.CreateProcess( command, cmdline, None, None, 1, flags, env, path, StartupInfo) try: doCreate() except pywintypes.error as pwte: if not _invalidWin32App(pwte): # This behavior isn't _really_ documented, but let's make it # consistent with the behavior that is documented. raise OSError(pwte) else: # look for a shebang line. Insert the original 'command' # (actually a script) into the new arguments list. sheb = _findShebang(command) if sheb is None: raise OSError( "%r is neither a Windows executable, " "nor a script with a shebang line" % command) else: args = list(args) args.insert(0, command) cmdline = quoteArguments(args) origcmd = command command = sheb try: # Let's try again. doCreate() except pywintypes.error as pwte2: # d'oh, failed again! if _invalidWin32App(pwte2): raise OSError( "%r has an invalid shebang line: " "%r is not a valid executable" % ( origcmd, sheb)) raise OSError(pwte2) # close handles which only the child will use win32file.CloseHandle(hStderrW) win32file.CloseHandle(hStdoutW) win32file.CloseHandle(hStdinR) # set up everything self.stdout = _pollingfile._PollableReadPipe( self.hStdoutR, lambda data: self.proto.childDataReceived(1, data), self.outConnectionLost) self.stderr = _pollingfile._PollableReadPipe( self.hStderrR, lambda data: self.proto.childDataReceived(2, data), self.errConnectionLost) self.stdin = _pollingfile._PollableWritePipe( self.hStdinW, self.inConnectionLost) for pipewatcher in self.stdout, self.stderr, self.stdin: self._addPollableResource(pipewatcher) # notify protocol self.proto.makeConnection(self) self._addPollableResource(_Reaper(self))
def __init__(self, reactor, protocol, command, args, environment, path): """ Create a new child process. """ _pollingfile._PollingTimer.__init__(self, reactor) BaseProcess.__init__(self, protocol) # security attributes for pipes sAttrs = win32security.SECURITY_ATTRIBUTES() sAttrs.bInheritHandle = 1 # create the pipes which will connect to the secondary process self.hStdoutR, hStdoutW = win32pipe.CreatePipe(sAttrs, 0) self.hStderrR, hStderrW = win32pipe.CreatePipe(sAttrs, 0) hStdinR, self.hStdinW = win32pipe.CreatePipe(sAttrs, 0) win32pipe.SetNamedPipeHandleState(self.hStdinW, win32pipe.PIPE_NOWAIT, None, None) # set the info structure for the new process. StartupInfo = win32process.STARTUPINFO() StartupInfo.hStdOutput = hStdoutW StartupInfo.hStdError = hStderrW StartupInfo.hStdInput = hStdinR StartupInfo.dwFlags = win32process.STARTF_USESTDHANDLES # Create new handles whose inheritance property is false currentPid = win32api.GetCurrentProcess() tmp = win32api.DuplicateHandle(currentPid, self.hStdoutR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdoutR) self.hStdoutR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStderrR, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStderrR) self.hStderrR = tmp tmp = win32api.DuplicateHandle(currentPid, self.hStdinW, currentPid, 0, 0, win32con.DUPLICATE_SAME_ACCESS) win32file.CloseHandle(self.hStdinW) self.hStdinW = tmp # Add the specified environment to the current environment - this is # necessary because certain operations are only supported on Windows # if certain environment variables are present. env = os.environ.copy() env.update(environment or {}) cmdline = quoteArguments(args) # TODO: error detection here. See #2787 and #4184. def doCreate(): self.hProcess, self.hThread, self.pid, dwTid = win32process.CreateProcess( command, cmdline, None, None, 1, 0, env, path, StartupInfo) try: try: doCreate() except TypeError as e: # win32process.CreateProcess cannot deal with mixed # str/unicode environment, so we make it all Unicode if e.args != ('All dictionary items must be strings, or ' 'all must be unicode', ): raise newenv = {} for key, value in env.items(): newenv[unicode(key)] = unicode(value) env = newenv doCreate() except pywintypes.error as pwte: if not _invalidWin32App(pwte): # This behavior isn't _really_ documented, but let's make it # consistent with the behavior that is documented. raise OSError(pwte) else: # look for a shebang line. Insert the original 'command' # (actually a script) into the new arguments list. sheb = _findShebang(command) if sheb is None: raise OSError("%r is neither a Windows executable, " "nor a script with a shebang line" % command) else: args = list(args) args.insert(0, command) cmdline = quoteArguments(args) origcmd = command command = sheb try: # Let's try again. doCreate() except pywintypes.error as pwte2: # d'oh, failed again! if _invalidWin32App(pwte2): raise OSError("%r has an invalid shebang line: " "%r is not a valid executable" % (origcmd, sheb)) raise OSError(pwte2) # close handles which only the child will use win32file.CloseHandle(hStderrW) win32file.CloseHandle(hStdoutW) win32file.CloseHandle(hStdinR) # set up everything self.stdout = _pollingfile._PollableReadPipe( self.hStdoutR, lambda data: self.proto.childDataReceived(1, data), self.outConnectionLost) self.stderr = _pollingfile._PollableReadPipe( self.hStderrR, lambda data: self.proto.childDataReceived(2, data), self.errConnectionLost) self.stdin = _pollingfile._PollableWritePipe(self.hStdinW, self.inConnectionLost) for pipewatcher in self.stdout, self.stderr, self.stdin: self._addPollableResource(pipewatcher) # notify protocol self.proto.makeConnection(self) self._addPollableResource(_Reaper(self))
def __init__(self, protocol): BaseProcess.__init__(self, protocol) if not signal.getsignal(signal.SIGCHLD): warnings.warn(error.PotentialZombieWarning.MESSAGE, error.PotentialZombieWarning, stacklevel=4)
def maybeCallProcessEnded(self): if self.closedNotifies == 3 and self.lostProcess: BaseProcess.maybeCallProcessEnded(self)