def main():
    """
    main entry point
    """
    initialize_stderr_logging()
    log = logging.getLogger("main")

    try:
        args = parse_commandline()
    except CommandlineError as instance:
        log.error("invalid commandline {0}".format(instance))
        return 1

    initialize_file_logging(args.log_path, args.verbose)

    if args.identity_path is not None:
        log.info("loading identity from {0}".format(args.identity_path))
        nimbusio_identity = \
            motoboto.identity.load_identity_from_file(args.identity_path)
        if nimbusio_identity is None:
            log.error("Unable to load identity from {0}".format(
                      args.identity_path))
            return 1
    else:
        nimbusio_identity = None

    halt_event = Event()
    set_signal_handler(halt_event)

    file_name_queue = queue.Queue()    

    try:
        notifier = create_notifier(args.watch_path, file_name_queue)
    except InotifyError as instance:
        log.error("Unable to initialize inotify: {0}".format(instance))
        return 1

    notifier_thread = create_notifier_thread(halt_event, notifier)
    notifier_thread.start()

    # we want the notifier running while we do the initial directory scan, so
    # we don't miss any files. But this leaves us open to duplicates()
    _initial_directory_scan(args.watch_path, file_name_queue)

    # so this eliminates duplicates, but leaks a little memory
    # if that's a problem, we could clean it out occasionally 
    file_name_set = set()

    log.info("main loop starts")
    return_code = 0
    while not halt_event.is_set():

        try:
            file_name = file_name_queue.get(block=True, timeout=1.0)
        except queue.Empty:
            continue

        log.debug("found file_name '{0}'".format(file_name))

        if args.pattern is not None:
            if not fnmatch.fnmatch(file_name, args.pattern):
                log.info("ignoring '{0}': does not match '{1}'".format(
                         file_name, args.pattern))
                continue

        if file_name in file_name_set:
            log.info("ignoring duplicate file name '{0}'".format(file_name))
            continue
        file_name_set.add(file_name)

        try:
            archive_file(args, nimbusio_identity, file_name)
            file_path = os.path.join(args.watch_path, file_name)
            log.info("deleting {0}".format(file_path))
            os.unlink(file_path)
        except Exception as instance:
            log.exception(instance)
            return_code = 1
            halt_event.set()

    log.info("main loop ends")
    notifier_thread.join(timeout=5.0)
#    assert not notifier_thread.is_alive
    notifier.stop()

    log.info("program terminates return_code = {0}".format(return_code))
    return return_code
def main():
    """
    main entry point for the program
    The return value will be the returncode for the program
    """
    initialize_stderr_logging()
    log = logging.getLogger("main")

    try:
        args = parse_commandline()
    except CommandlineError:
        instance = sys.exc_info()[1]
        log.error("invalid commandline {0}".format(instance))
        return 1

    if not "(?P<key>" in args.key_regex:
        log.error("invalid key regex {0}".format(args.key_regex))
        return 1

    try:
        key_regex = re.compile(args.key_regex)
    except Exception:
        instance = sys.exc_info()[1]
        log.error("Unable to compile key_regex {0} {1}".format(args.key_regex,
                                                               instance))
        return 1        

    initialize_file_logging(args.log_path, args.verbose)

    log.info("key regex pattern = '{0}'".format(key_regex.pattern))

    halt_event = Event()
    set_signal_handler(halt_event)

    file_name_queue = queue.Queue()    

    try:
        notifier = create_notifier(args.watch_path, file_name_queue)
    except InotifyError as instance:
        log.error("Unable to initialize inotify: {0}".format(instance))
        return 1

    try:
        redis = create_redis_connection()
    except Exception:
        log.exception("Unable to connect to redis")
        return 1

    # clear REDIS of all keys under our namespace, so that old sets that 
    # don't have any files anymore don't stay around
    existing_keys = redis.keys("_".join([args.redis_prefix, "*"]))
    if len(existing_keys) > 0: 
        log.debug("deleting {0} existing REDIS keys".format(
                  len(existing_keys), ))
        redis.delete(*existing_keys)

    # set our up-to-date time as far back as we can: we aren't up to date yet
    up_to_date_timestamp_key = "_".join([args.redis_prefix, 
                                         _up_to_date_timestamp])
    log.debug("setting {0} to {1}".format(up_to_date_timestamp_key, 0))
    redis.set(up_to_date_timestamp_key, "0")

    notifier_thread = create_notifier_thread(halt_event, 
                                             notifier, 
                                             file_name_queue)

    # we want the notifier running while we do the initial directory scan, so
    # we don't miss any files. 
    notifier_thread.start()

    _initial_directory_scan(args.watch_path, file_name_queue)

    log.info("main loop starts")
    directory_scan_up_to_date = False
    up_to_date = False
    return_code = 0
    while not halt_event.is_set():

        try:
            file_name, event_name = file_name_queue.get(block=True, timeout=1.0)
        except queue.Empty:
            continue
        except KeyboardInterrupt:
            log.warn("KeyboardInterrupt: halting")
            halt_event.set()
            break

        if event_name == directory_scan_finished:
            log.debug("setting directory_scan_up_to_date")
            directory_scan_up_to_date = True
            continue

        if event_name == inotify_idle:
            if not up_to_date and directory_scan_up_to_date:
                current_time = int(time.time())
                log.debug("setting {0} to {1}".format(up_to_date_timestamp_key, 
                                                      current_time))
                redis.set(up_to_date_timestamp_key, str(current_time))
                up_to_date = True
            continue

        match_object = key_regex.match(file_name)
        if match_object is None:
            log.debug("unmatched file name '{0}'".format(file_name))
            continue
        key = match_object.group("key")

        log.debug("found file_name '{0}' key {1} event {2}".format(file_name, 
                                                                   key,
                                                                   event_name))
        redis_key = "_".join([args.redis_prefix, key, ])
        try:
            _dispatch_table[event_name](redis, redis_key, file_name)
        except Exception:
            log.exception("{0} {1}".format(file_name, event_name))
            return_code = 1
            halt_event.set()

    log.info("main loop ends")
    redis.set(up_to_date_timestamp_key, "0")
    notifier_thread.join(timeout=5.0)
#    assert not notifier_thread.is_alive
    notifier.stop()

    log.info("program terminates return_code = {0}".format(return_code))
    return return_code