def _execute_local(self): """ Run command. """ # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable(program_to_execute) if not command_full_path: raise ValueError( "The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c'] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None return (return_code, error_msg)
def _execute_local(self): """ Run command. """ # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable( program_to_execute ) if not command_full_path: raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None return (return_code, error_msg)
def _execute_local(self): """ Run command. """ #self._logger.info('executing %s...', self.options['command']) # start_time = time.time() # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] command_full_path = find_executable( program_to_execute ) if not command_full_path: raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute) self._process = \ ShellProc(self.options['command'], self.stdin, self.stdout, self.stderr, self.options['env_vars']) #self._logger.debug('PID = %d', self._process.pid) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None # et = time.time() - start_time #if et >= 60: #pragma no cover #self._logger.info('elapsed time: %.1f sec.', et) return (return_code, error_msg)
def test_errormsg(self): logging.debug('') logging.debug('test_errormsg') cmd = 'dir' if sys.platform == 'win32' else 'ls' try: proc = ShellProc(cmd, stdout='stdout', stderr='stderr') proc.wait() finally: if os.path.exists('stdout'): os.remove('stdout') if os.path.exists('stderr'): os.remove('stderr') msg = proc.error_message(-signal.SIGTERM) if sys.platform == 'win32': self.assertEqual(msg, '') else: self.assertEqual(msg, ': SIGTERM')
def _execute_local(self): """ Run command. """ #self._logger.info('executing %s...', self.options['command']) # start_time = time.time() # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable(program_to_execute) if not command_full_path: raise ValueError( "The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c'] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) #self._logger.debug('PID = %d', self._process.pid) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None # et = time.time() - start_time #if et >= 60: #pragma no cover #self._logger.info('elapsed time: %.1f sec.', et) return (return_code, error_msg)
def _start_tunnel(address, port, args, user, identity, prefix): """ Start an ssh tunnel process. """ if '@' in address: user, host = address.split('@') else: user = user or getpass.getuser() host = address if sys.platform == 'win32': # pragma no cover cmd = ['plink', '-batch', '-ssh'] else: cmd = ['ssh'] cmd += ['-l', user] if identity: cmd += ['-i', identity] cmd += ['-N', '-x', '-T'] # plink doesn't support '-n' (no stdin) cmd += args + [host] logname = '%s-%s-%s.log' % (prefix, host, port) logname = os.path.join(os.getcwd(), logname) stdout = open(logname, 'w') tunnel_proc = None try: tunnel_proc = ShellProc(cmd, stdout=stdout, stderr=STDOUT) except Exception as exc: raise RuntimeError("Can't create ssh tunnel process from %s: %s" % (cmd, exc)) time.sleep(1) exitcode = tunnel_proc.poll() if exitcode is not None: raise RuntimeError('ssh tunnel process for %s:%s exited with exitcode' ' %d, output in %s' % (address, port, exitcode, logname)) return (_cleanup_tunnel, tunnel_proc, stdout, logname, os.getpid())
def _execute_local(self): """ Run command. """ #self._logger.info('executing %s...', self.options['command']) # start_time = time.time() # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable( program_to_execute ) if not command_full_path: raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) #self._logger.debug('PID = %d', self._process.pid) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None # et = time.time() - start_time #if et >= 60: #pragma no cover #self._logger.info('elapsed time: %.1f sec.', et) return (return_code, error_msg)
class ExternalCode(Component): """ Run an external code as a component Default stdin is the 'null' device, default stdout is the console, and default stderr is ``error.out``. Options ------- fd_options['force_fd'] : bool(False) Set to True to finite difference this system. fd_options['form'] : str('forward') Finite difference mode. (forward, backward, central) You can also set to 'complex_step' to peform the complex step method if your components support it. fd_options['step_size'] : float(1e-06) Default finite difference stepsize fd_options['step_type'] : str('absolute') Set to absolute, relative options['check_external_outputs'] : bool(True) Check that all input or output external files exist options['command'] : list([]) command to be executed options['env_vars'] : dict({}) Environment variables required by the command options['external_input_files'] : list([]) (optional) list of input file names to check the pressence of before solve_nonlinear options['external_output_files'] : list([]) (optional) list of input file names to check the pressence of after solve_nonlinear options['poll_delay'] : float(0.0) Delay between polling for command completion. A value of zero will use an internally computed default options['timeout'] : float(0.0) Maximum time to wait for command completion. A value of zero implies an infinite wait """ def __init__(self): super(ExternalCode, self).__init__() self.STDOUT = STDOUT self.DEV_NULL = DEV_NULL # Input options for this Component self.options = OptionsDictionary() self.options.add_option('command', [], desc='command to be executed') self.options.add_option('env_vars', {}, desc='Environment variables required by the command') self.options.add_option('poll_delay', 0.0, lower=0.0, desc='Delay between polling for command completion. A value of zero will use an internally computed default') self.options.add_option('timeout', 0.0, lower=0.0, desc='Maximum time to wait for command completion. A value of zero implies an infinite wait') self.options.add_option('check_external_outputs', True, desc='Check that all input or output external files exist') self.options.add_option( 'external_input_files', [], desc='(optional) list of input file names to check the pressence of before solve_nonlinear') self.options.add_option( 'external_output_files', [], desc='(optional) list of input file names to check the pressence of after solve_nonlinear') # Outputs of the run of the component or items that will not work with the OptionsDictionary self.return_code = 0 # Return code from the command self.timed_out = False # True if the command timed-out self.stdin = self.DEV_NULL self.stdout = None self.stderr = "error.out" def check_setup(self, out_stream=sys.stdout): """Write a report to the given stream indicating any potential problems found with the current configuration of this ``Problem``. Args ---- out_stream : a file-like object, optional """ # check for the command if not self.options['command']: out_stream.write( "The command cannot be empty") else: if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] command_full_path = find_executable( program_to_execute ) if not command_full_path: msg = "The command to be executed, '%s', cannot be found" % program_to_execute out_stream.write(msg) # Check for missing input files missing_files = self._check_for_files(input=True) for iotype, path in missing_files: msg = "The %s file %s is missing" % ( iotype, path ) out_stream.write(msg) def solve_nonlinear(self, params, unknowns, resids): """Runs the component """ self.return_code = -12345678 self.timed_out = False if not self.options['command']: raise ValueError('Empty command list') # self.check_files(inputs=True) return_code = None error_msg = '' try: return_code, error_msg = self._execute_local() if return_code is None: # if self._stop: # raise RuntimeError('Run stopped') # else: self.timed_out = True raise RuntimeError('Timed out') elif return_code: if isinstance(self.stderr, str): if os.path.exists(self.stderr): stderrfile = open(self.stderr, 'r') error_desc = stderrfile.read() stderrfile.close() err_fragment = "\nError Output:\n%s" % error_desc else: err_fragment = "\n[stderr %r missing]" % self.stderr else: err_fragment = error_msg raise RuntimeError('return_code = %d%s' % (return_code, err_fragment)) if self.options['check_external_outputs']: missing_files = self._check_for_files(input=False) msg = "" for iotype, path in missing_files: msg += "%s file %s is missing\n" % (iotype, path) if msg: raise RuntimeError( "Missing files: %s" % msg ) # self.check_files(inputs=False) finally: self.return_code = -999999 if return_code is None else return_code def _check_for_files(self, input=True): """ Check that all 'specific' input external files exist. input: bool If True, check inputs. Else check outputs """ missing_files = [] if input: files = self.options['external_input_files'] else: files = self.options['external_output_files'] for path in files: if not os.path.exists(path): missing_files.append(('input', path)) return missing_files def _execute_local(self): """ Run command. """ #self._logger.info('executing %s...', self.options['command']) # start_time = time.time() # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable( program_to_execute ) if not command_full_path: raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) #self._logger.debug('PID = %d', self._process.pid) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None # et = time.time() - start_time #if et >= 60: #pragma no cover #self._logger.info('elapsed time: %.1f sec.', et) return (return_code, error_msg)
def start_server(address='localhost', port=None, allowed_hosts=None, debug=False, server_out=None, args=()): """ Start server process at `address` and `port`. Returns ``(proc, port)``. address: string Server address to be used. port: int Server port to be used. Use zero for a system-selected port. allowed_hosts: list[string] Hosts to allow access. If None then ``['127.0.0.1', socket.gethostname()]`` is used. debug: bool Set logging level to ``DEBUG``, default is ``INFO``. args: iter of str Other command line args to pass to server. """ if port is None: port = get_open_address()[1] if allowed_hosts is None: allowed_hosts = ['127.0.0.1', socket.gethostname()] with open('hosts.allow', 'w') as out: for pattern in allowed_hosts: out.write('%s\n' % pattern) if sys.platform != 'win32' or HAVE_PYWIN32: make_private('hosts.allow') server_path = os.path.splitext(os.path.abspath(__file__))[0]+'.py' if server_out is None: server_out = 'as-%d.out' % port server_up = 'as-%d.up' % port if os.path.exists(server_up): os.remove(server_up) # Start process. args = [sys.executable, server_path, '-d', '--address', address, '--port', '%d' % port, '--up', server_up] + args if debug: args.append('--debug') logging.info("start_server: args=%s" % args) #print("starting ShellProc:",args) proc = ShellProc(args, stdout=server_out, stderr=STDOUT) # Wait for valid server_up file. timeout = 30 # Seconds. retry = 0 while (not os.path.exists(server_up)) or \ (os.path.getsize(server_up) == 0): return_code = proc.poll() if return_code: error_msg = proc.error_message(return_code) raise RuntimeError('Server startup failed: exit code %s (%s)' % (return_code, error_msg)) retry += 1 if retry < 10*timeout: time.sleep(.1) # Hard to cause a startup timeout. else: # pragma no cover proc.terminate(timeout) raise RuntimeError('Server startup timeout') # Read server information. with open(server_up, 'r') as inp: host = inp.readline().strip() port = int(inp.readline().strip()) pid = int(inp.readline().strip()) os.remove(server_up) return (proc, port)
class ExternalCode(Component): """ Run an external code as a component Default stdin is the 'null' device, default stdout is the console, and default stderr is ``error.out``. Options ------- fd_options['force_fd'] : bool(False) Set to True to finite difference this system. fd_options['form'] : str('forward') Finite difference mode. (forward, backward, central) You can also set to 'complex_step' to peform the complex step method if your components support it. fd_options['step_size'] : float(1e-06) Default finite difference stepsize fd_options['step_type'] : str('absolute') Set to absolute, relative fd_options['extra_check_partials_form'] : None or str Finite difference mode: ("forward", "backward", "central", "complex_step") During check_partial_derivatives, you can optionally do a second finite difference with a different mode. fd_options['linearize'] : bool(False) Set to True if you want linearize to be called even though you are using FD. options['command'] : list([]) Command to be executed. Command must be a list of command line args. options['env_vars'] : dict({}) Environment variables required by the command options['external_input_files'] : list([]) (optional) list of input file names to check the existence of before solve_nonlinear options['external_output_files'] : list([]) (optional) list of input file names to check the existence of after solve_nonlinear options['poll_delay'] : float(0.0) Delay between polling for command completion. A value of zero will use an internally computed default. options['timeout'] : float(0.0) Maximum time in seconds to wait for command completion. A value of zero implies an infinite wait. If the timeout interval is exceeded, an AnalysisError will be raised. options['fail_hard'] : bool(True) Behavior on error returned from code, either raise a 'hard' error (RuntimeError) if True or a 'soft' error (AnalysisError) if False. """ def __init__(self): super(ExternalCode, self).__init__() self.STDOUT = STDOUT self.DEV_NULL = DEV_NULL # Input options for this Component self.options = OptionsDictionary() self.options.add_option('command', [], desc='command to be executed') self.options.add_option( 'env_vars', {}, desc='Environment variables required by the command') self.options.add_option( 'poll_delay', 0.0, lower=0.0, desc= 'Delay between polling for command completion. A value of zero will use an internally computed default' ) self.options.add_option( 'timeout', 0.0, lower=0.0, desc= 'Maximum time to wait for command completion. A value of zero implies an infinite wait' ) self.options.add_option( 'external_input_files', [], desc= '(optional) list of input file names to check the existence of before solve_nonlinear' ) self.options.add_option( 'external_output_files', [], desc= '(optional) list of input file names to check the existence of after solve_nonlinear' ) self.options.add_option( 'fail_hard', True, desc= "If True, external code errors raise a 'hard' exception (RuntimeError). Otherwise raise a 'soft' exception (AnalysisError)." ) # Outputs of the run of the component or items that will not work with the OptionsDictionary self.return_code = 0 # Return code from the command self.stdin = self.DEV_NULL self.stdout = None self.stderr = "error.out" def check_setup(self, out_stream=sys.stdout): """Write a report to the given stream indicating any potential problems found with the current configuration of this ``Problem``. Args ---- out_stream : a file-like object, optional """ # check for the command cmd = [c for c in self.options['command'] if c.strip()] if not cmd: out_stream.write("The command cannot be empty") else: program_to_execute = self.options['command'][0] command_full_path = find_executable(program_to_execute) if not command_full_path: out_stream.write("The command to be executed, '%s', " "cannot be found" % program_to_execute) # Check for missing input files missing = self._check_for_files(self.options['external_input_files']) if missing: out_stream.write("The following input files are missing at setup " " time: %s" % missing) def solve_nonlinear(self, params, unknowns, resids): """Runs the component """ self.return_code = -12345678 if not self.options['command']: raise ValueError('Empty command list') if self.options['fail_hard']: err_class = RuntimeError else: err_class = AnalysisError return_code = None try: missing = self._check_for_files( self.options['external_input_files']) if missing: raise err_class("The following input files are missing: %s" % sorted(missing)) return_code, error_msg = self._execute_local() if return_code is None: raise AnalysisError('Timed out after %s sec.' % self.options['timeout']) elif return_code: if isinstance(self.stderr, str): if os.path.exists(self.stderr): stderrfile = open(self.stderr, 'r') error_desc = stderrfile.read() stderrfile.close() err_fragment = "\nError Output:\n%s" % error_desc else: err_fragment = "\n[stderr %r missing]" % self.stderr else: err_fragment = error_msg raise err_class('return_code = %d%s' % (return_code, err_fragment)) missing = self._check_for_files( self.options['external_output_files']) if missing: raise err_class("The following output files are missing: %s" % sorted(missing)) finally: self.return_code = -999999 if return_code is None else return_code def _check_for_files(self, files): """ Check that specified files exist. """ return [path for path in files if not os.path.exists(path)] def _execute_local(self): """ Run command. """ # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable(program_to_execute) if not command_full_path: raise ValueError( "The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c'] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None return (return_code, error_msg)
class ExternalCode(Component): """ Run an external code as a component Default stdin is the 'null' device, default stdout is the console, and default stderr is ``error.out``. Options ------- deriv_options['type'] : str('user') Derivative calculation type ('user', 'fd', 'cs') Default is 'user', where derivative is calculated from user-supplied derivatives. Set to 'fd' to finite difference this system. Set to 'cs' to perform the complex step if your components support it. deriv_options['form'] : str('forward') Finite difference mode. (forward, backward, central) deriv_options['step_size'] : float(1e-06) Default finite difference stepsize deriv_options['step_calc'] : str('absolute') Set to absolute, relative deriv_options['check_type'] : str('fd') Type of derivative check for check_partial_derivatives. Set to 'fd' to finite difference this system. Set to 'cs' to perform the complex step method if your components support it. deriv_options['check_form'] : str('forward') Finite difference mode: ("forward", "backward", "central") During check_partial_derivatives, the difference form that is used for the check. deriv_options['check_step_calc'] : str('absolute',) Set to 'absolute' or 'relative'. Default finite difference step calculation for the finite difference check in check_partial_derivatives. deriv_options['check_step_size'] : float(1e-06) Default finite difference stepsize for the finite difference check in check_partial_derivatives" deriv_options['linearize'] : bool(False) Set to True if you want linearize to be called even though you are using FD. options['command'] : list([]) Command to be executed. Command must be a list of command line args. options['env_vars'] : dict({}) Environment variables required by the command options['external_input_files'] : list([]) (optional) list of input file names to check the existence of before solve_nonlinear options['external_output_files'] : list([]) (optional) list of input file names to check the existence of after solve_nonlinear options['poll_delay'] : float(0.0) Delay between polling for command completion. A value of zero will use an internally computed default. options['timeout'] : float(0.0) Maximum time in seconds to wait for command completion. A value of zero implies an infinite wait. If the timeout interval is exceeded, an AnalysisError will be raised. options['fail_hard'] : bool(True) Behavior on error returned from code, either raise a 'hard' error (RuntimeError) if True or a 'soft' error (AnalysisError) if False. """ def __init__(self): super(ExternalCode, self).__init__() self.STDOUT = STDOUT self.DEV_NULL = DEV_NULL # Input options for this Component self.options = OptionsDictionary() self.options.add_option('command', [], desc='command to be executed') self.options.add_option('env_vars', {}, desc='Environment variables required by the command') self.options.add_option('poll_delay', 0.0, lower=0.0, desc='Delay between polling for command completion. A value of zero will use an internally computed default') self.options.add_option('timeout', 0.0, lower=0.0, desc='Maximum time to wait for command completion. A value of zero implies an infinite wait') self.options.add_option( 'external_input_files', [], desc='(optional) list of input file names to check the existence of before solve_nonlinear') self.options.add_option( 'external_output_files', [], desc='(optional) list of input file names to check the existence of after solve_nonlinear') self.options.add_option('fail_hard', True, desc="If True, external code errors raise a 'hard' exception (RuntimeError). Otherwise raise a 'soft' exception (AnalysisError).") # Outputs of the run of the component or items that will not work with the OptionsDictionary self.return_code = 0 # Return code from the command self.stdin = self.DEV_NULL self.stdout = None self.stderr = "error.out" def check_setup(self, out_stream=sys.stdout): """Write a report to the given stream indicating any potential problems found with the current configuration of this ``Problem``. Args ---- out_stream : a file-like object, optional """ # check for the command cmd = [c for c in self.options['command'] if c.strip()] if not cmd: out_stream.write( "The command cannot be empty") else: program_to_execute = self.options['command'][0] command_full_path = find_executable( program_to_execute ) if not command_full_path: out_stream.write("The command to be executed, '%s', " "cannot be found" % program_to_execute) # Check for missing input files missing = self._check_for_files(self.options['external_input_files']) if missing: out_stream.write("The following input files are missing at setup " " time: %s" % missing) def solve_nonlinear(self, params, unknowns, resids): """Runs the component """ self.return_code = -12345678 if not self.options['command']: raise ValueError('Empty command list') if self.options['fail_hard']: err_class = RuntimeError else: err_class = AnalysisError return_code = None try: missing = self._check_for_files(self.options['external_input_files']) if missing: raise err_class("The following input files are missing: %s" % sorted(missing)) return_code, error_msg = self._execute_local() if return_code is None: raise AnalysisError('Timed out after %s sec.' % self.options['timeout']) elif return_code: if isinstance(self.stderr, str): if os.path.exists(self.stderr): stderrfile = open(self.stderr, 'r') error_desc = stderrfile.read() stderrfile.close() err_fragment = "\nError Output:\n%s" % error_desc else: err_fragment = "\n[stderr %r missing]" % self.stderr else: err_fragment = error_msg raise err_class('return_code = %d%s' % (return_code, err_fragment)) missing = self._check_for_files(self.options['external_output_files']) if missing: raise err_class("The following output files are missing: %s" % sorted(missing)) finally: self.return_code = -999999 if return_code is None else return_code def _check_for_files(self, files): """ Check that specified files exist. """ return [path for path in files if not os.path.exists(path)] def _execute_local(self): """ Run command. """ # check to make sure command exists if isinstance(self.options['command'], str): program_to_execute = self.options['command'] else: program_to_execute = self.options['command'][0] # suppress message from find_executable function, we'll handle it numpy.distutils.log.set_verbosity(-1) command_full_path = find_executable( program_to_execute ) if not command_full_path: raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute) command_for_shell_proc = self.options['command'] if sys.platform == 'win32': command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc self._process = \ ShellProc(command_for_shell_proc, self.stdin, self.stdout, self.stderr, self.options['env_vars']) try: return_code, error_msg = \ self._process.wait(self.options['poll_delay'], self.options['timeout']) finally: self._process.close_files() self._process = None return (return_code, error_msg)