Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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')
Ejemplo n.º 5
0
    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')
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
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())
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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)