Actually execute the program.

    Calling this method can be done from tests to simulate executing the
    application from command line.

    Parameters:
    options       -- `optionparser` from config file.
    exit_codeword -- an optional exit_message that will shut down Rewind. Used
                     for testing.

    returns -- exit code for the application. Non-zero for errors.

def run(options, exit_codeword=None):
    """Actually execute the program.

    Calling this method can be done from tests to simulate executing the
    application from command line.

    Parameters:
    options       -- `optionparser` from config file.
    exit_codeword -- an optional exit_message that will shut down Rewind. Used
                     for testing.

    returns -- exit code for the application. Non-zero for errors.

    """
    QUERY_ENDP_OPT = 'query-bind-endpoint'
    STREAM_ENDP_OPT = 'streaming-bind-endpoint'
    ZMQ_NTHREADS = "zmq-nthreads"

    if not options.has_section(config.DEFAULT_SECTION):
        msg = "Missing default section, `{0}`."
        fmsg = msg.format(config.DEFAULT_SECTION)
        raise config.ConfigurationError(fmsg)

    if not options.has_option(config.DEFAULT_SECTION, QUERY_ENDP_OPT):
        msg = "Missing (query) bind endpoint in option file: {0}:{1}"
        fmsg = msg.format(config.DEFAULT_SECTION, QUERY_ENDP_OPT)
        raise config.ConfigurationError(fmsg)

    queryendp = options.get(config.DEFAULT_SECTION, QUERY_ENDP_OPT).split(",")
    streamendp = _get_with_fallback(options, config.DEFAULT_SECTION,
                                    STREAM_ENDP_OPT, '').split(",")
    queryendp = filter(lambda x: x.strip(), queryendp)
    streamendp = filter(lambda x: x.strip(), streamendp)

    try:
        eventstore = config.construct_eventstore(options)
    except config.ConfigurationError as e:
        _logger.exception("Could instantiate event store from config file.")
        raise

    zmq_nthreads = _get_with_fallback(options, config.DEFAULT_SECTION,
                                      ZMQ_NTHREADS, '3')
    try:
        zmq_nthreads = int(zmq_nthreads)
    except ValueError:
        msg = "{0}:{1} must be an integer".format(config.DEFAULT_SECTION,
                                                  ZMQ_NTHREADS)
        _logger.fatal(msg)
        return 1

    with _zmq_context_context(zmq_nthreads) as context, \
            _zmq_socket_context(context, zmq.REP, queryendp) as querysock, \
            _zmq_socket_context(context, zmq.PUB,
                                streamendp) as streamsock:
        # Executing the program in the context of ZeroMQ context as well as
        # ZeroMQ sockets. Using with here to make sure are correctly closing
        # things in the correct order, particularly also if we have an
        # exception or similar.

        runner = _RewindRunner(eventstore, querysock, streamsock,
                               (exit_codeword.encode()
                                if exit_codeword
                                else None))
        runner.run()

    return 0