20 import signal
as signal_module
23 is_list_like, is_truthy, NormalizedDict, py2to3,
24 secs_to_timestr, system_decode, system_encode,
25 timestr_to_secs, IRONPYTHON, JYTHON, WINDOWS)
293 ROBOT_LIBRARY_SCOPE =
'GLOBAL'
295 TERMINATE_TIMEOUT = 30
299 self.
_processes_processes = ConnectionCache(
'No active process.')
333 timeout = configuration.pop(
'timeout',
None)
334 on_timeout = configuration.pop(
'on_timeout',
'terminate')
336 handle = self.
start_processstart_process(command, *arguments, **configuration)
356 command = conf.get_command(command, list(arguments))
358 process = subprocess.Popen(command, **conf.popen_config)
360 return self.
_processes_processes.register(process, alias=conf.alias)
365 logger.info(
u'Starting process:\n%s' %
system_decode(command))
366 logger.debug(
u'Process configuration:\n%s' % config)
375 return self.
_processes_processes[handle].poll()
is None
384 error_message='Process is not running.'):
395 error_message='Process is running.'):
444 logger.info(
'Waiting for process to complete.')
448 logger.info(
'Process did not complete in %s.'
451 return self.
_wait_wait(process)
454 if on_timeout ==
'terminate':
456 elif on_timeout ==
'kill':
459 logger.info(
'Leaving process intact.')
463 result = self.
_results_results[process]
464 result.rc = process.wait()
or 0
465 result.close_streams()
466 logger.info(
'Process completed.')
504 if not hasattr(process,
'terminate'):
505 raise RuntimeError(
'Terminating processes is not supported '
506 'by this Python version.')
513 logger.debug(
'Ignored OSError because process was stopped.')
514 return self.
_wait_wait(process)
517 logger.info(
'Forcefully killing process.')
518 if hasattr(os,
'killpg'):
519 os.killpg(process.pid, signal_module.SIGKILL)
523 raise RuntimeError(
'Failed to kill process.')
526 logger.info(
'Gracefully terminating process.')
529 if hasattr(os,
'killpg'):
530 os.killpg(process.pid, signal_module.SIGTERM)
531 elif hasattr(signal_module,
'CTRL_BREAK_EVENT'):
534 ctypes.windll.kernel32.GenerateConsoleCtrlEvent(
535 signal_module.CTRL_BREAK_EVENT, process.pid)
537 process.send_signal(signal_module.CTRL_BREAK_EVENT)
541 logger.info(
'Graceful termination failed.')
542 self.
_kill_kill(process)
554 for handle
in range(1, len(self.
_processes_processes) + 1):
588 raise RuntimeError(
'This keyword does not work on Windows.')
591 logger.info(
'Sending signal %s (%d).' % (signal, signum))
592 if is_truthy(group)
and hasattr(os,
'killpg'):
593 os.killpg(process.pid, signum)
594 elif hasattr(process,
'send_signal'):
595 process.send_signal(signum)
597 raise RuntimeError(
'Sending signals is not supported '
598 'by this Python version.')
602 return int(int_or_name)
608 return getattr(signal_module,
609 name
if name.startswith(
'SIG')
else 'SIG' + name)
610 except AttributeError:
611 raise RuntimeError(
"Unsupported signal '%s'." % name)
670 stderr=False, stdout_path=False, stderr_path=False):
672 if result.rc
is None:
673 raise RuntimeError(
'Getting results of unfinished processes '
676 stdout_path, stderr_path)
679 elif len(attributes) == 1:
684 attributes = (result.rc, result.stdout, result.stderr,
685 result.stdout_path, result.stderr_path)
686 includes = (
is_truthy(incl)
for incl
in includes)
687 return tuple(attr
for attr, incl
in zip(attributes, includes)
if incl)
705 stopped =
lambda: process.poll()
is not None
706 max_time = time.time() + timeout
707 while time.time() <= max_time
and not stopped():
708 time.sleep(min(0.1, timeout))
746 return subprocess.list2cmdline(args)
751 def __init__(self, process, stdout, stderr, rc=None, output_encoding=None):
766 return stream
not in (subprocess.PIPE, subprocess.STDOUT)
772 if self.
_stdout_stdout
is None:
780 if self.
_stderr_stderr
is None:
792 stream = open(stream_path,
'rb')
793 elif not self.
_is_open_is_open(stream):
796 content = stream.read()
805 return stream
and not stream.closed
809 output = output.replace(
'\r\n',
'\n')
810 if output.endswith(
'\n'):
821 stdin, stdout, stderr = process.stdin, process.stdout, process.stderr
826 return [stdin, stdout, stderr]
829 return '<result object with rc %d>' % self.
rcrc
835 def __init__(self, cwd=None, shell=False, stdout=None, stderr=None,
836 output_encoding='CONSOLE', alias=None, env=None, **rest):
847 return cwd.replace(
'/', os.sep)
852 name = name.replace(
'/', os.sep)
853 return open(os.path.join(self.
cwdcwd, name),
'w')
854 return subprocess.PIPE
857 if stderr
and stderr
in [
'STDOUT', stdout]:
858 if stdout_stream != subprocess.PIPE:
860 return subprocess.STDOUT
868 env = NormalizedDict(env, spaceless=
False)
871 env =
dict((key.upper(), env[key])
for key
in env)
878 return os.environ.copy()
883 if not key.startswith(
'env:'):
884 raise RuntimeError(
"Keyword argument '%s' is not supported by "
885 "this keyword." % key)
889 command = [
system_encode(item)
for item
in [command] + arguments]
890 if not self.
shellshell:
893 return subprocess.list2cmdline(command)
897 popen_config = property
902 'stdin': subprocess.PIPE,
903 'shell': self.
shellshell,
909 config[
'close_fds'] =
True
915 if hasattr(os,
'setsid'):
916 config[
'preexec_fn'] = os.setsid
917 if hasattr(subprocess,
'CREATE_NEW_PROCESS_GROUP'):
918 config[
'creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
921 result_config = property
939 if hasattr(stream,
'name'):
941 return {subprocess.PIPE:
'PIPE',
942 subprocess.STDOUT:
'STDOUT'}.get(stream, stream)
def _is_open(self, stream)
def _format_output(self, output)
def _read_stream(self, stream_path, stream)
def _get_path(self, stream)
def __init__(self, process, stdout, stderr, rc=None, output_encoding=None)
def _get_and_read_standard_streams(self, process)
def _is_custom_stream(self, stream)
def get_command(self, command, arguments)
def _add_process_group_config(self, config)
def _new_stream(self, name)
def _add_to_env(self, env, extra)
def _stream_name(self, stream)
def _get_stderr(self, stderr, stdout, stdout_stream)
def __init__(self, cwd=None, shell=False, stdout=None, stderr=None, output_encoding='CONSOLE', alias=None, env=None, **rest)
def _get_initial_env(self, env, extra)
def _construct_env(self, env, extra)
Robot Framework test library for running processes.
def _convert_signal_name_to_number(self, name)
def terminate_process(self, handle=None, kill=False)
Stops the process gracefully or forcefully.
def split_command_line(self, args, escaping=False)
Splits command line string into a list of arguments.
def get_process_result(self, handle=None, rc=False, stdout=False, stderr=False, stdout_path=False, stderr_path=False)
Returns the specified result object or some of its attributes.
def _terminate(self, process)
def terminate_all_processes(self, kill=False)
Terminates all still running processes started by this library.
def process_should_be_stopped(self, handle=None, error_message='Process is running.')
Verifies that the process is not running.
def run_process(self, command, *arguments, **configuration)
Runs a process and waits for it to complete.
def join_command_line(self, *args)
Joins arguments into one command line string.
def _process_is_stopped(self, process, timeout)
def start_process(self, command, *arguments, **configuration)
Starts a new process on background.
def _get_signal_number(self, int_or_name)
def get_process_id(self, handle=None)
Returns the process ID (pid) of the process as an integer.
def _manage_process_timeout(self, handle, on_timeout)
def send_signal_to_process(self, signal, handle=None, group=False)
Sends the given signal to the specified process.
def _get_result_attributes(self, result, *includes)
def wait_for_process(self, handle=None, timeout=None, on_timeout='continue')
Waits for the process to complete or to reach the given timeout.
def switch_process(self, handle)
Makes the specified process the current active process.
def _log_start(self, command, config)
def process_should_be_running(self, handle=None, error_message='Process is not running.')
Verifies that the process is running.
def get_process_object(self, handle=None)
Return the underlying subprocess.Popen object.
def is_process_running(self, handle=None)
Checks is the process running or not.
def cmdline2list(args, escaping=False)
def system_decode(string)
Decodes bytes from system (e.g.
def console_decode(string, encoding=CONSOLE_ENCODING, force=False)
Decodes bytes from console encoding to Unicode.
def system_encode(string, errors='replace')
Encodes Unicode to system encoding (e.g.
def abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
def secs_to_timestr(secs, compact=False)
Converts time in seconds to a string representation.
def timestr_to_secs(timestr, round_to=3)
Parses time like '1h 10s', '01:00:10' or '42' and returns seconds.
def is_truthy(item)
Returns True or False depending is the item considered true or not.
def get_version(naked=False)