@classmethod
    def from_columns(cls, columns, nrows=0, fill=False):

        if not isinstance(columns, ColDefs):
            columns = ColDefs(columns)

        # read the delayed data
        for column in columns:
            arr = column.array
            if isinstance(arr, Delayed):
                if arr.hdu.data is None:
                    column.array = None
                else:
                    column.array = _get_recarray_field(arr.hdu.data,
                                                       arr.field)
        # Reset columns._arrays (which we may want to just do away with
        # altogether
        del columns._arrays

        # use the largest column shape as the shape of the record
        if nrows == 0:
            for arr in columns._arrays:
                if arr is not None:
                    dim = arr.shape[0]
                else:
                    dim = 0
                if dim > nrows:
                    nrows = dim

        raw_data = np.empty(columns.dtype.itemsize * nrows, dtype=np.uint8)
        raw_data.fill(ord(columns._padding_byte))
        data = np.recarray(nrows, dtype=columns.dtype, buf=raw_data).view(cls)

        # Make sure the data is a listener for changes to the columns
        columns._add_listener(data)

        # Previously this assignment was made from hdu.columns, but that's a
        # bug since if a _TableBaseHDU has a FITS_rec in its .data attribute
        # the _TableBaseHDU.columns property is actually returned from
        # .data._coldefs, so this assignment was circular!  Don't make that
        # mistake again.
        # All of this is an artifact of the fragility of the FITS_rec class,
        # and that it can't just be initialized by columns...
        data._coldefs = columns

        # If fill is True we don't copy anything from the column arrays.  We're
        # just using them as a template, and returning a table filled with
        # zeros/blanks
        if fill:
            return data

        # Otherwise we have to fill the recarray with data from the input
        # columns
        for idx, column in enumerate(columns):
            # For each column in the ColDef object, determine the number of
            # rows in that column.  This will be either the number of rows in
            # the ndarray associated with the column, or the number of rows
            # given in the call to this function, which ever is smaller.  If
            # the input FILL argument is true, the number of rows is set to
            # zero so that no data is copied from the original input data.
            arr = column.array

            if arr is None:
                array_size = 0
            else:
                array_size = len(arr)

            n = min(array_size, nrows)

            # TODO: At least *some* of this logic is mostly redundant with the
            # _convert_foo methods in this class; see if we can eliminate some
            # of that duplication.

            if not n:
                # The input column had an empty array, so just use the fill
                # value
                continue

            field = _get_recarray_field(data, idx)
            name = column.name
            fitsformat = column.format
            recformat = fitsformat.recformat

            outarr = field[:n]
            inarr = arr[:n]

            if isinstance(recformat, _FormatX):
                # Data is a bit array
                if inarr.shape[-1] == recformat.repeat:
                    _wrapx(inarr, outarr, recformat.repeat)
                    continue
            elif isinstance(recformat, _FormatP):
                data._cache_field(name, _makep(inarr, field, recformat,
                                               nrows=nrows))
                continue
            # TODO: Find a better way of determining that the column is meant
            # to be FITS L formatted
            elif recformat[-2:] == FITS2NUMPY['L'] and inarr.dtype == bool:
                # column is boolean
                # The raw data field should be filled with either 'T' or 'F'
                # (not 0).  Use 'F' as a default
                field[:] = ord('F')
                # Also save the original boolean array in data._converted so
                # that it doesn't have to be re-converted
                converted = np.zeros(field.shape, dtype=bool)
                converted[:n] = inarr
                data._cache_field(name, converted)
                # TODO: Maybe this step isn't necessary at all if _scale_back
                # will handle it?
                inarr = np.where(inarr == False, ord('F'), ord('T'))
            elif (columns[idx]._physical_values and
                    columns[idx]._pseudo_unsigned_ints):
                # Temporary hack...
                bzero = column.bzero
                converted = np.zeros(field.shape, dtype=inarr.dtype)
                converted[:n] = inarr
                data._cache_field(name, converted)
                if n < nrows:
                    # Pre-scale rows below the input data
                    field[n:] = -bzero

                inarr = inarr - bzero
            elif isinstance(columns, _AsciiColDefs):
                # Regardless whether the format is character or numeric, if the
                # input array contains characters then it's already in the raw
                # format for ASCII tables
                if fitsformat._pseudo_logical:
                    # Hack to support converting from 8-bit T/F characters
                    # Normally the column array is a chararray of 1 character
                    # strings, but we need to view it as a normal ndarray of
                    # 8-bit ints to fill it with ASCII codes for 'T' and 'F'
                    outarr = field.view(np.uint8, np.ndarray)[:n]
                elif arr.dtype.kind not in ('S', 'U'):
                    # Set up views of numeric columns with the appropriate
                    # numeric dtype
                    # Fill with the appropriate blanks for the column format
                    data._cache_field(name, np.zeros(nrows, dtype=arr.dtype))
                    outarr = data._converted[name][:n]

                outarr[:] = inarr
                continue

            if inarr.shape != outarr.shape:
                if (inarr.dtype.kind == outarr.dtype.kind and
                        inarr.dtype.kind in ('U', 'S') and
                        inarr.dtype != outarr.dtype):

                    inarr_rowsize = inarr[0].size
                    inarr = inarr.flatten().view(outarr.dtype)

                # This is a special case to handle input arrays with
                # non-trivial TDIMn.
                # By design each row of the outarray is 1-D, while each row of
                # the input array may be n-D
                if outarr.ndim > 1:
                    # The normal case where the first dimension is the rows
                    inarr_rowsize = inarr[0].size
                    inarr = inarr.reshape((n, inarr_rowsize))
                    outarr[:, :inarr_rowsize] = inarr
                else:
                    # Special case for strings where the out array only has one
                    # dimension (the second dimension is rolled up into the
                    # strings
                    outarr[:n] = inarr.ravel()
            else:
                outarr[:] = inarr

        # Now replace the original column array references with the new
        # fields
        # This is required to prevent the issue reported in
        # https://github.com/spacetelescope/PyFITS/issues/99
        for idx in range(len(columns)):
            columns._arrays[idx] = data.field(idx)

        return data
iterates over dependency edges with transiability from item
dataset/ETHPy150Open spacetelescope/PyFITS/pyfits/fitsrec.py/FITS_rec.from_columns--crossover with--dataset/ETHPy150Open anchor/make-magic/core/deptools.py/DigraphDependencyStrategy.edges_from_item_deps