зеркало из https://github.com/mozilla/gecko-dev.git
Bug 943481 - Mirror mozprocess 0.14 from mozbase github;r=wlach
This commit is contained in:
Родитель
e62e9b8dc4
Коммит
374dd9f02b
|
@ -2,8 +2,6 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import logging
|
||||
import mozinfo
|
||||
import os
|
||||
import select
|
||||
import signal
|
||||
|
@ -19,7 +17,11 @@ __all__ = ['ProcessHandlerMixin', 'ProcessHandler']
|
|||
# Set the MOZPROCESS_DEBUG environment variable to 1 to see some debugging output
|
||||
MOZPROCESS_DEBUG = os.getenv("MOZPROCESS_DEBUG")
|
||||
|
||||
if mozinfo.isWin:
|
||||
# We dont use mozinfo because it is expensive to import, see bug 933558.
|
||||
isWin = os.name == "nt"
|
||||
isPosix = os.name == "posix" # includes MacOS X
|
||||
|
||||
if isWin:
|
||||
import ctypes, ctypes.wintypes, msvcrt
|
||||
from ctypes import sizeof, addressof, c_ulong, byref, POINTER, WinError, c_longlong
|
||||
import winprocess
|
||||
|
@ -31,8 +33,8 @@ class ProcessHandlerMixin(object):
|
|||
"""
|
||||
A class for launching and manipulating local processes.
|
||||
|
||||
:param cmd: command to run.
|
||||
:param args: is a list of arguments to pass to the command (defaults to None).
|
||||
:param cmd: command to run. May be a string or a list. If specified as a list, the first element will be interpreted as the command, and all additional elements will be interpreted as arguments to that command.
|
||||
:param args: list of arguments to pass to the command (defaults to None). Must not be set when `cmd` is specified as a list.
|
||||
:param cwd: working directory for command (defaults to None).
|
||||
:param env: is the environment to use for the process (defaults to os.environ).
|
||||
:param ignore_children: causes system to ignore child processes when True, defaults to False (which tracks child processes).
|
||||
|
@ -77,7 +79,7 @@ class ProcessHandlerMixin(object):
|
|||
# Parameter for whether or not we should attempt to track child processes
|
||||
self._ignore_children = ignore_children
|
||||
|
||||
if not self._ignore_children and not mozinfo.isWin:
|
||||
if not self._ignore_children and not isWin:
|
||||
# Set the process group id for linux systems
|
||||
# Sets process group id to the pid of the parent process
|
||||
# NOTE: This prevents you from using preexec_fn and managing
|
||||
|
@ -97,7 +99,7 @@ class ProcessHandlerMixin(object):
|
|||
raise
|
||||
|
||||
def __del__(self, _maxint=sys.maxint):
|
||||
if mozinfo.isWin:
|
||||
if isWin:
|
||||
if self._handle:
|
||||
if hasattr(self, '_internal_poll'):
|
||||
self._internal_poll(_deadstate=_maxint)
|
||||
|
@ -110,7 +112,7 @@ class ProcessHandlerMixin(object):
|
|||
|
||||
def kill(self):
|
||||
self.returncode = 0
|
||||
if mozinfo.isWin:
|
||||
if isWin:
|
||||
if not self._ignore_children and self._handle and self._job:
|
||||
winprocess.TerminateJobObject(self._job, winprocess.ERROR_CONTROL_C_EXIT)
|
||||
self.returncode = winprocess.GetExitCodeProcess(self._handle)
|
||||
|
@ -155,7 +157,7 @@ class ProcessHandlerMixin(object):
|
|||
|
||||
""" Private Members of Process class """
|
||||
|
||||
if mozinfo.isWin:
|
||||
if isWin:
|
||||
# Redefine the execute child so that we can track process groups
|
||||
def _execute_child(self, args, executable, preexec_fn, close_fds,
|
||||
cwd, env, universal_newlines, startupinfo,
|
||||
|
@ -510,7 +512,7 @@ falling back to not using job objects for managing child processes"""
|
|||
else:
|
||||
self._handle = None
|
||||
|
||||
elif mozinfo.isMac or mozinfo.isUnix:
|
||||
elif isPosix:
|
||||
|
||||
def _wait(self):
|
||||
""" Haven't found any reason to differentiate between these platforms
|
||||
|
@ -521,8 +523,20 @@ falling back to not using job objects for managing child processes"""
|
|||
|
||||
if not self._ignore_children:
|
||||
try:
|
||||
# os.waitpid returns a (pid, status) tuple
|
||||
return os.waitpid(self.pid, 0)[1]
|
||||
# os.waitpid return value:
|
||||
# > [...] a tuple containing its pid and exit status
|
||||
# > indication: a 16-bit number, whose low byte is the
|
||||
# > signal number that killed the process, and whose
|
||||
# > high byte is the exit status (if the signal number
|
||||
# > is zero)
|
||||
# - http://docs.python.org/2/library/os.html#os.wait
|
||||
status = os.waitpid(self.pid, 0)[1]
|
||||
|
||||
# For consistency, format status the same as subprocess'
|
||||
# returncode attribute
|
||||
if status > 255:
|
||||
return status >> 8
|
||||
return -status
|
||||
except OSError, e:
|
||||
if getattr(e, "errno", None) != 10:
|
||||
# Error 10 is "no child process", which could indicate normal
|
||||
|
@ -582,11 +596,12 @@ falling back to not using job objects for managing child processes"""
|
|||
|
||||
# It is common for people to pass in the entire array with the cmd and
|
||||
# the args together since this is how Popen uses it. Allow for that.
|
||||
if not isinstance(self.cmd, list):
|
||||
self.cmd = [self.cmd]
|
||||
|
||||
if self.args:
|
||||
self.cmd = self.cmd + self.args
|
||||
if isinstance(self.cmd, list):
|
||||
if self.args != None:
|
||||
raise TypeError("cmd and args must not both be lists")
|
||||
(self.cmd, self.args) = (self.cmd[0], self.cmd[1:])
|
||||
elif self.args is None:
|
||||
self.args = []
|
||||
|
||||
@property
|
||||
def timedOut(self):
|
||||
|
@ -624,7 +639,7 @@ falling back to not using job objects for managing child processes"""
|
|||
args.update(self.keywordargs)
|
||||
|
||||
# launch the process
|
||||
self.proc = self.Process(self.cmd, **args)
|
||||
self.proc = self.Process([self.cmd] + self.args, **args)
|
||||
|
||||
self.processOutput(timeout=timeout, outputTimeout=outputTimeout)
|
||||
|
||||
|
@ -701,9 +716,13 @@ falling back to not using job objects for managing child processes"""
|
|||
lineReadTimeout = outputTimeout
|
||||
|
||||
(lines, self.didTimeout) = self.readWithTimeout(logsource, lineReadTimeout)
|
||||
while lines != "" and not self.didTimeout:
|
||||
while lines != "":
|
||||
for line in lines.splitlines():
|
||||
self.processOutputLine(line.rstrip())
|
||||
|
||||
if self.didTimeout:
|
||||
break
|
||||
|
||||
if timeout:
|
||||
lineReadTimeout = timeout - (datetime.now() - self.startTime).seconds
|
||||
(lines, self.didTimeout) = self.readWithTimeout(logsource, lineReadTimeout)
|
||||
|
@ -732,6 +751,10 @@ falling back to not using job objects for managing child processes"""
|
|||
If timeout is not None, will return after timeout seconds.
|
||||
This timeout only causes the wait function to return and
|
||||
does not kill the process.
|
||||
|
||||
Returns the process' exit code. A None value indicates the
|
||||
process hasn't terminated yet. A negative value -N indicates
|
||||
the process was killed by signal N (Unix only).
|
||||
"""
|
||||
if self.outThread:
|
||||
# Thread.join() blocks the main thread until outThread is finished
|
||||
|
@ -754,7 +777,7 @@ falling back to not using job objects for managing child processes"""
|
|||
|
||||
### Private methods from here on down. Thar be dragons.
|
||||
|
||||
if mozinfo.isWin:
|
||||
if isWin:
|
||||
# Windows Specific private functions are defined in this block
|
||||
PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
|
||||
GetLastError = ctypes.windll.kernel32.GetLastError
|
||||
|
@ -795,7 +818,9 @@ falling back to not using job objects for managing child processes"""
|
|||
|
||||
output = os.read(f.fileno(), 4096)
|
||||
if not output:
|
||||
return (self.read_buffer, False)
|
||||
output = self.read_buffer
|
||||
self.read_buffer = ''
|
||||
return (output, False)
|
||||
self.read_buffer += output
|
||||
if '\n' not in self.read_buffer:
|
||||
time.sleep(0.01)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
from setuptools import setup
|
||||
|
||||
PACKAGE_VERSION = '0.13'
|
||||
PACKAGE_VERSION = '0.14'
|
||||
|
||||
setup(name='mozprocess',
|
||||
version=PACKAGE_VERSION,
|
||||
|
|
|
@ -12,3 +12,4 @@ disabled = bug 877864
|
|||
disabled = bug 921632
|
||||
[test_mozprocess_misc.py]
|
||||
[test_mozprocess_wait.py]
|
||||
[test_mozprocess_nonewline.py]
|
||||
|
|
|
@ -136,6 +136,48 @@ class ProcTest(unittest.TestCase):
|
|||
p.proc.returncode,
|
||||
p.didTimeout)
|
||||
|
||||
def test_commandline_no_args(self):
|
||||
"""Command line is reported correctly when no arguments are specified"""
|
||||
p = processhandler.ProcessHandler(self.proclaunch, cwd=here)
|
||||
self.assertEqual(p.commandline, self.proclaunch)
|
||||
|
||||
def test_commandline_overspecified(self):
|
||||
"""Command line raises an exception when the arguments are specified ambiguously"""
|
||||
err = None
|
||||
try:
|
||||
p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
|
||||
args=["1", "2", "3"],
|
||||
cwd=here)
|
||||
except TypeError, e:
|
||||
err = e
|
||||
|
||||
self.assertTrue(err)
|
||||
|
||||
def test_commandline_from_list(self):
|
||||
"""Command line is reported correctly when command and arguments are specified in a list"""
|
||||
p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
|
||||
cwd=here)
|
||||
self.assertEqual(p.commandline, self.proclaunch + ' process_normal_finish.ini')
|
||||
|
||||
def test_commandline_over_specified(self):
|
||||
"""Command line raises an exception when the arguments are specified ambiguously"""
|
||||
err = None
|
||||
try:
|
||||
p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
|
||||
args=["1", "2", "3"],
|
||||
cwd=here)
|
||||
except TypeError, e:
|
||||
err = e
|
||||
|
||||
self.assertTrue(err)
|
||||
|
||||
def test_commandline_from_args(self):
|
||||
"""Command line is reported correctly when arguments are specified in a dedicated list"""
|
||||
p = processhandler.ProcessHandler(self.proclaunch,
|
||||
args=["1", "2", "3"],
|
||||
cwd=here)
|
||||
self.assertEqual(p.commandline, self.proclaunch + ' 1 2 3')
|
||||
|
||||
def test_process_wait(self):
|
||||
"""Process is started runs to completion while we wait indefinitely"""
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
import proctest
|
||||
import mozinfo
|
||||
from mozprocess import processhandler
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
@ -51,6 +51,11 @@ class ProcTestWait(proctest.ProcTest):
|
|||
p.wait()
|
||||
|
||||
detected, output = proctest.check_for_process(self.proclaunch)
|
||||
|
||||
if mozinfo.isUnix:
|
||||
# process was killed, so returncode should be negative
|
||||
self.assertLess(p.proc.returncode, 0)
|
||||
|
||||
self.determine_status(detected,
|
||||
output,
|
||||
p.proc.returncode,
|
||||
|
|
Загрузка…
Ссылка в новой задаче