Read a headerless tabular text file into a stream of Holders.

    Arguments:

    descpath
      The path of the table description ini file.
    descsection
      The section in the description file to use.
    tabpath
      The path to the actual table data.
    tabwidth=8
      The tab width to assume. Please don't monkey with it.
    mode='rt'
      The table file open mode (passed to io.open()).
    noexistok=False
      If True and the file is missing, treat it as empty.
    ``**kwargs``
      Passed to io.open ().

    Returns a generator of a stream of `pwkit.Holder`s, each of which will
    contain ints, strings, or some kind of measurement (cf `pwkit.msmt`). In
    this version, the table file does not contain a header, as seen in Vizier
    data files. The corresponding section in the description ini file has keys
    of the form "colname = <start> <end> [type]", where <start> and <end> are
    the **1-based** character numbers defining the column, and [type] is an
    optional specified of the measurement type of the column (one of the usual
    b, i, f, u, Lu, Pu).

def vizread (descpath, descsection, tabpath, tabwidth=8, **kwargs):
    """Read a headerless tabular text file into a stream of Holders.

    Arguments:

    descpath
      The path of the table description ini file.
    descsection
      The section in the description file to use.
    tabpath
      The path to the actual table data.
    tabwidth=8
      The tab width to assume. Please don't monkey with it.
    mode='rt'
      The table file open mode (passed to io.open()).
    noexistok=False
      If True and the file is missing, treat it as empty.
    ``**kwargs``
      Passed to io.open ().

    Returns a generator of a stream of `pwkit.Holder`s, each of which will
    contain ints, strings, or some kind of measurement (cf `pwkit.msmt`). In
    this version, the table file does not contain a header, as seen in Vizier
    data files. The corresponding section in the description ini file has keys
    of the form "colname = <start> <end> [type]", where <start> and <end> are
    the **1-based** character numbers defining the column, and [type] is an
    optional specified of the measurement type of the column (one of the usual
    b, i, f, u, Lu, Pu).

    """
    from .inifile import read as iniread

    cols = []

    for i in iniread (descpath):
        if i.section != descsection:
            continue

        for field, desc in six.iteritems (i.__dict__):
            if field == 'section':
                continue

            a = desc.split ()
            idx0 = int (a[0]) - 1

            if len (a) == 1:
                cols.append ((field, slice (idx0, idx0 + 1), msmt.parsers['s']))
                continue

            if len (a) == 2:
                parser = msmt.parsers['s']
            else:
                parser = msmt.parsers[a[2]]

            cols.append ((field, slice (idx0, int (a[1])), parser))

    for text in _trimmedlines (tabpath, **kwargs):
        text = text.expandtabs (tabwidth)

        h = Holder ()
        for name, cslice, parser in cols:
            try:
                v = parser (text[cslice].strip ())
            except:
                reraise_context ('while parsing "%s"', text[cslice].strip ())
            h.set_one (name, v)

        yield h