to_argv_schema(instructions) yields a valid tuple of CommandLineParser instructions for the
      given instructions tuple; by itself, this will only return the instructions as they are, but
      optional arguments (below) will override the values in the instructions if provided.
    to_argv_schema(plan) yields a valid tuple of CommandLineParser instructions for the given plan
      object.

    These schema returned by this function will parse a command-line list (sys.argv) for parameters
    either listed in the instructions (see help(CommandLineParser)) or the afferent parameters of
    the plan. Generally, this should be called by the argv_parse() function and not directly.

    If a plan is given as the first argument, then the following rules are used to determine how
    arguments are parsed:
      * An argument that begins with -- (e.g., --quux-factor=10) is checked for a matching
        plan parameter; the argument name "quux-factor" will match either a parameter called
        "quux-factor" or "quux_factor" (dashes in command-line arguments are auto-translated
        into underscores).
      * If "quux_factor" is a parameter to the plan and is the only parameter that starts with a
        'q', then -q10 or -q 10 are both equivalent to --quux-factor=10. If other parameters
        also start with a 'q' then neither "quux_factor" or the other parameter(s) will be
        matched with the -q flag unless it is specified explicitly via the arg_abbrevs option.
      * Argument values are parsed using Python's ast.literal_eval(); if this raises an
        exception then the value is left as a string.
      * If an argument or flag is provided without an argument (e.g. "--quuxztize" or "-q") then
        it is interpreted as a boolean flag and is given the value True.
      * Arguments that come after the flag "--" are never processed.

    The following options may be given:
      * arg_names (default: None) may be a dictionary that specifies explicity command-line
        argument names for the plan parameters; plan parameters should be keys and the argument
        names should be values. Any parameter not listed in this option will be interpreted
        according to the above rules. If a parameter is mapped to None then it will not be
        filled from the command-line arguments.
      * arg_abbrevs (default:None) may be a dictionary that is handled identically to that of
        arg_names except that its values must be single letters, which are used for the
        abbreviated flag names.
      * defaults (default: None) may specify the default values for the plan parameters; this
        dictionary overrides the default values of the plan itself.

def to_argv_schema(data, arg_names=None, arg_abbrevs=None, filters=None, defaults=None):
    '''
    to_argv_schema(instructions) yields a valid tuple of CommandLineParser instructions for the
      given instructions tuple; by itself, this will only return the instructions as they are, but
      optional arguments (below) will override the values in the instructions if provided.
    to_argv_schema(plan) yields a valid tuple of CommandLineParser instructions for the given plan
      object.

    These schema returned by this function will parse a command-line list (sys.argv) for parameters
    either listed in the instructions (see help(CommandLineParser)) or the afferent parameters of
    the plan. Generally, this should be called by the argv_parse() function and not directly.

    If a plan is given as the first argument, then the following rules are used to determine how
    arguments are parsed:
      * An argument that begins with -- (e.g., --quux-factor=10) is checked for a matching
        plan parameter; the argument name "quux-factor" will match either a parameter called
        "quux-factor" or "quux_factor" (dashes in command-line arguments are auto-translated
        into underscores).
      * If "quux_factor" is a parameter to the plan and is the only parameter that starts with a
        'q', then -q10 or -q 10 are both equivalent to --quux-factor=10. If other parameters
        also start with a 'q' then neither "quux_factor" or the other parameter(s) will be
        matched with the -q flag unless it is specified explicitly via the arg_abbrevs option.
      * Argument values are parsed using Python's ast.literal_eval(); if this raises an
        exception then the value is left as a string.
      * If an argument or flag is provided without an argument (e.g. "--quuxztize" or "-q") then
        it is interpreted as a boolean flag and is given the value True.
      * Arguments that come after the flag "--" are never processed.

    The following options may be given:
      * arg_names (default: None) may be a dictionary that specifies explicity command-line
        argument names for the plan parameters; plan parameters should be keys and the argument
        names should be values. Any parameter not listed in this option will be interpreted
        according to the above rules. If a parameter is mapped to None then it will not be
        filled from the command-line arguments.
      * arg_abbrevs (default:None) may be a dictionary that is handled identically to that of
        arg_names except that its values must be single letters, which are used for the
        abbreviated flag names.
      * defaults (default: None) may specify the default values for the plan parameters; this
        dictionary overrides the default values of the plan itself.
    '''
    if is_plan(data):
        # First we must convert it to a valid instruction list
        (plan, data) = (data, {})
        # we go through the afferent parameters...
        for aff in plan.afferents:
            # these are provided by the parsing mechanism and shouldn't be processed
            if aff in ['argv', 'argv_parsed', 'stdout', 'stderr', 'stdin']: continue
            # we ignore defaults for now
            data[aff] = (None, aff.replace('_', '-'), aff)
        # and let's try to guess at abbreviation names
        entries = sorted(data.keys())
        n = len(entries)
        for (ii,entry) in enumerate(entries):
            if ii > 0   and entry[0] == entries[ii-1][0]: continue
            if ii < n-1 and entry[0] == entries[ii+1][0]: continue
            r = data[entry]
            data[entry] = (entry[0], r[1], entry, r[3]) if len(r) == 4 else (entry[0], r[1], entry)
        # now go through and fix defaults...
        for (entry,dflt) in six.iteritems(plan.defaults):
            if entry not in data: continue
            r = data[entry]
            data[entry] = (r[0], r[1], r[2], dflt)
    elif arg_names is None and arg_abbrevs is None and defaults is None:
        # return the same object if there are no changes to a schema
        return data
    else:
        data = {r[2]:r for r in data}
    # Now we go through and make updates based on the optional arguments
    if arg_names is None: arg_names = {}
    for (entry,arg_name) in six.iteritems(arg_names):
        if entry not in data: continue
        r = data[entry]
        data[entry] = (r[0], arg_name, entry) if len(r) == 3 else (r[0], arg_name, entry, r[3])
    if arg_abbrevs is None: arg_abbrevs = {}
    for (entry,arg_abbrev) in six.iteritems(arg_abbrevs):
        if entry not in data: continue
        r = data[entry]
        data[entry] = (arg_abbrev, r[1], entry) if len(r) == 3 else (arg_abbrev, r[1], entry, r[3])
    if defaults is None: defaults = {}
    for (entry,dflt) in six.iteritems(defaults):
        if entry not in data: continue
        r = data[entry]
        data[entry] = (r[0], r[1], entry, dflt)
    # return the list-ified version of this
    return [tuple(row) for row in six.itervalues(data)]