17 import signal
as signal_module
20 from tempfile
import TemporaryFile
22 from robot.utils import (abspath, cmdline2list, ConnectionCache, console_decode,
23 console_encode, is_list_like, is_pathlike, is_string,
24 is_truthy, NormalizedDict, secs_to_timestr, system_decode,
25 system_encode, timestr_to_secs, WINDOWS)
319 ROBOT_LIBRARY_SCOPE =
'GLOBAL'
321 TERMINATE_TIMEOUT = 30
325 self.
_processes_processes = ConnectionCache(
'No active process.')
359 timeout = configuration.pop(
'timeout',
None)
360 on_timeout = configuration.pop(
'on_timeout',
'terminate')
362 handle = self.
start_processstart_process(command, *arguments, **configuration)
406 command = conf.get_command(command, list(arguments))
408 process = subprocess.Popen(command, **conf.popen_config)
410 self.
_processes_processes.register(process, alias=conf.alias)
416 logger.info(
'Starting process:\n%s' %
system_decode(command))
417 logger.debug(
'Process configuration:\n%s' % config)
426 return self.
_processes_processes[handle].poll()
is None
435 error_message='Process is not running.'):
446 error_message='Process is running.'):
500 logger.info(
'Waiting for process to complete.')
504 logger.info(
'Process did not complete in %s.'
507 return self.
_wait_wait(process)
510 if (
is_string(timeout)
and timeout.upper() ==
'NONE')
or not timeout:
515 if on_timeout ==
'terminate':
517 elif on_timeout ==
'kill':
520 logger.info(
'Leaving process intact.')
524 result = self.
_results_results[process]
525 result.rc = process.wait()
or 0
526 result.close_streams()
527 logger.info(
'Process completed.')
562 if not hasattr(process,
'terminate'):
563 raise RuntimeError(
'Terminating processes is not supported '
564 'by this Python version.')
571 logger.debug(
'Ignored OSError because process was stopped.')
572 return self.
_wait_wait(process)
575 logger.info(
'Forcefully killing process.')
576 if hasattr(os,
'killpg'):
577 os.killpg(process.pid, signal_module.SIGKILL)
584 logger.info(
'Gracefully terminating process.')
587 if hasattr(os,
'killpg'):
588 os.killpg(process.pid, signal_module.SIGTERM)
589 elif hasattr(signal_module,
'CTRL_BREAK_EVENT'):
590 process.send_signal(signal_module.CTRL_BREAK_EVENT)
594 logger.info(
'Graceful termination failed.')
595 self.
_kill_kill(process)
607 for handle
in range(1, len(self.
_processes_processes) + 1):
640 raise RuntimeError(
'This keyword does not work on Windows.')
643 logger.info(
'Sending signal %s (%d).' % (signal, signum))
644 if is_truthy(group)
and hasattr(os,
'killpg'):
645 os.killpg(process.pid, signum)
646 elif hasattr(process,
'send_signal'):
647 process.send_signal(signum)
650 'by this Python version.')
654 return int(int_or_name)
660 return getattr(signal_module,
661 name
if name.startswith(
'SIG')
else 'SIG' + name)
662 except AttributeError:
727 stderr=False, stdout_path=False, stderr_path=False):
729 if result.rc
is None:
730 raise RuntimeError(
'Getting results of unfinished processes '
733 stdout_path, stderr_path)
736 elif len(attributes) == 1:
741 attributes = (result.rc, result.stdout, result.stderr,
742 result.stdout_path, result.stderr_path)
743 includes = (
is_truthy(incl)
for incl
in includes)
744 return tuple(attr
for attr, incl
in zip(attributes, includes)
if incl)
762 stopped =
lambda: process.poll()
is not None
763 max_time = time.time() + timeout
764 while time.time() <= max_time
and not stopped():
765 time.sleep(min(0.1, timeout))
799 return subprocess.list2cmdline(args)
804 def __init__(self, process, stdout, stderr, stdin=None, rc=None,
805 output_encoding=None):
820 return stream
not in (subprocess.PIPE, subprocess.STDOUT,
None)
826 if self.
_stdout_stdout
is None:
834 if self.
_stderr_stderr
is None:
846 stream = open(stream_path,
'rb')
847 elif not self.
_is_open_is_open(stream):
850 content = stream.read()
859 return stream
and not stream.closed
863 output = output.replace(
'\r\n',
'\n')
864 if output.endswith(
'\n'):
875 stdin, stdout, stderr = process.stdin, process.stdout, process.stderr
880 return [stdin, stdout, stderr]
883 return '<result object with rc %d>' % self.
rcrc
888 def __init__(self, cwd=None, shell=False, stdout=None, stderr=None, stdin='PIPE',
889 output_encoding='CONSOLE', alias=None, env=None, **rest):
900 if name ==
'DEVNULL':
901 return open(os.devnull,
'w')
903 path = os.path.normpath(os.path.join(self.
cwdcwd, name))
904 return open(path,
'w')
905 return subprocess.PIPE
908 if stderr
and stderr
in [
'STDOUT', stdout]:
909 if stdout_stream != subprocess.PIPE:
911 return subprocess.STDOUT
919 elif stdin.upper() ==
'NONE':
921 elif stdin ==
'PIPE':
922 return subprocess.PIPE
923 path = os.path.normpath(os.path.join(self.
cwdcwd, stdin))
924 if os.path.isfile(path):
926 stdin_file = TemporaryFile()
936 env = NormalizedDict(env, spaceless=
False)
939 env = dict((key.upper(), env[key])
for key
in env)
946 return os.environ.copy()
951 if not key.startswith(
'env:'):
952 raise RuntimeError(
"Keyword argument '%s' is not supported by "
953 "this keyword." % key)
957 command = [
system_encode(item)
for item
in [command] + arguments]
958 if not self.
shellshell:
961 return subprocess.list2cmdline(command)
965 popen_config = property
971 'shell': self.
shellshell,
977 config[
'close_fds'] =
True
982 if hasattr(os,
'setsid'):
983 config[
'preexec_fn'] = os.setsid
984 if hasattr(subprocess,
'CREATE_NEW_PROCESS_GROUP'):
985 config[
'creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
988 result_config = property
1004 env: %s""" % (self.
cwdcwd,
1013 if hasattr(stream,
'name'):
1015 return {subprocess.PIPE:
'PIPE',
1016 subprocess.STDOUT:
'STDOUT',
1017 None:
'None'}.get(stream, stream)
def _format_output(self, output)
def _get_path(self, stream)
def __init__(self, process, stdout, stderr, stdin=None, rc=None, output_encoding=None)
def _is_custom_stream(self, stream)
def _get_and_read_standard_streams(self, process)
def _is_open(self, stream)
def _read_stream(self, stream_path, stream)
def _get_stderr(self, stderr, stdout, stdout_stream)
def _construct_env(self, env, extra)
def _new_stream(self, name)
def __init__(self, cwd=None, shell=False, stdout=None, stderr=None, stdin='PIPE', output_encoding='CONSOLE', alias=None, env=None, **rest)
def _get_initial_env(self, env, extra)
def get_command(self, command, arguments)
def _add_to_env(self, env, extra)
def _stream_name(self, stream)
def _get_stdin(self, stdin)
def _add_process_group_config(self, config)
Robot Framework library for running processes.
def _manage_process_timeout(self, handle, on_timeout)
def _terminate(self, process)
def get_process_id(self, handle=None)
Returns the process ID (pid) of the process as an integer.
def start_process(self, command, *arguments, **configuration)
Starts a new process on background.
def _get_signal_number(self, int_or_name)
def run_process(self, command, *arguments, **configuration)
Runs a process and waits for it to complete.
def _log_start(self, command, config)
def _get_result_attributes(self, result, *includes)
def _process_is_stopped(self, process, timeout)
def terminate_all_processes(self, kill=False)
Terminates all still running processes started by this library.
def is_process_running(self, handle=None)
Checks is the process running or not.
def _convert_signal_name_to_number(self, name)
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 process_should_be_stopped(self, handle=None, error_message='Process is running.')
Verifies that the process is not running.
def terminate_process(self, handle=None, kill=False)
Stops the process gracefully or forcefully.
def switch_process(self, handle)
Makes the specified process the current active process.
def send_signal_to_process(self, signal, handle=None, group=False)
Sends the given signal to the specified process.
def _get_timeout(self, timeout)
def join_command_line(self, *args)
Joins arguments into one command line string.
def get_process_object(self, handle=None)
Return the underlying subprocess.Popen object.
def split_command_line(self, args, escaping=False)
Splits command line string into a list of arguments.
def process_should_be_running(self, handle=None, error_message='Process is not running.')
Verifies that the process is running.
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 cmdline2list(args, escaping=False)
def console_decode(string, encoding=CONSOLE_ENCODING)
Decodes bytes from console encoding to Unicode.
def system_encode(string)
def system_decode(string)
def console_encode(string, encoding=None, errors='replace', stream=sys.__stdout__, force=False)
Encodes the given string so that it can be used in the console.
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, accept_plain_values=True)
Parses time strings like '1h 10s', '01:00:10' and '42' and returns seconds.
def is_truthy(item)
Returns True or False depending on is the item considered true or not.
def get_version(naked=False)