code: def concat(blocks: List["pyarrow.Table"]) -> "pyarrow.Table":
      """Concatenate provided Arrow Tables into a single Arrow Table. This has special
      handling for extension types that pyarrow.concat_tables does not yet support.
      """
      import pyarrow as pa
  
      from ray.data.extensions import (
          ArrowTensorArray,
          ArrowTensorType,
          ArrowVariableShapedTensorType,
      )
  
      if not blocks:
          # Short-circuit on empty list of blocks.
          return blocks
  
      if len(blocks) == 1:
          return blocks[0]
  
      # Rollup columns with opaque (null-typed) lists, to process in following for-loop.
      cols_with_null_list = set()
      for b in blocks:
          for col_name in b.schema.names:
              col_type = b.schema.field(col_name).type
              if pa.types.is_list(col_type) and pa.types.is_null(
                  col_type.value_type
              ):
                  cols_with_null_list.add(col_name)
  
      schema = blocks[0].schema
      if (
          any(
              isinstance(type_, pa.ExtensionType)
              for type_ in schema.types
          )
          or cols_with_null_list
      ):
          # Custom handling for extension array columns.
          cols = []
          for col_name in schema.names:
              col_chunked_arrays = []
              for block in blocks:
                  col_chunked_arrays.append(block.column(col_name))
              if isinstance(
                  schema.field(col_name).type,
                  (ArrowTensorType, ArrowVariableShapedTensorType),
              ):
                  # For our tensor extension types, manually construct a chunked array
                  # containing chunks from all blocks. This is to handle
                  # homogeneous-shaped block columns having different shapes across
                  # blocks: if tensor element shapes differ across blocks, a
                  # variable-shaped tensor array will be returned.
                  col = ArrowTensorArray._chunk_tensor_arrays(
                      [
                          chunk
                          for ca in col_chunked_arrays
                          for chunk in ca.chunks
                      ]
                  )
              else:
                  if col_name in cols_with_null_list:
                      # For each opaque list column, iterate through all schemas until
                      # we find a valid value_type that can be used to override the
                      # column types in the following for-loop.
                      scalar_type = None
                      for arr in col_chunked_arrays:
                          if not pa.types.is_list(
                              arr.type
                          ) or not pa.types.is_null(
                              arr.type.value_type
                          ):
                              scalar_type = arr.type
                              break
  
                      if scalar_type is not None:
                          for c_idx in range(len(col_chunked_arrays)):
                              c = col_chunked_arrays[c_idx]
                              if pa.types.is_list(
                                  c.type
                              ) and pa.types.is_null(c.type.value_type):
                                  if pa.types.is_list(scalar_type):
                                      # If we are dealing with a list input,
                                      # cast the array to the scalar_type found above.
                                      col_chunked_arrays[
                                          c_idx
                                      ] = c.cast(scalar_type)
                                  else:
                                      # If we are dealing with a single value, construct
                                      # a new array with null values filled.
                                      col_chunked_arrays[
                                          c_idx
                                      ] = pa.chunked_array(
                                          [
                                              pa.nulls(
                                                  c.length(),
                                                  type=scalar_type,
                                              )
                                          ]
                                      )
                  col = _concatenate_chunked_arrays(col_chunked_arrays)
              cols.append(col)
  
          # If the result contains pyarrow schemas, unify them
          schemas_to_unify = [b.schema for b in blocks]
          if pyarrow is not None and any(
              isinstance(s, pyarrow.Schema) for s in schemas_to_unify
          ):
              schema = unify_schemas(schemas_to_unify)
          else:
              # Otherwise, if the resulting schemas are simple types (e.g. int),
              # check that all blocks with valid schemas have the same type.
              schema = schemas_to_unify[0]
              if schema is not None:
                  for s in schemas_to_unify:
                      if s is not None and s != schema:
                          raise ValueError(
                              "Found blocks with different types "
                              f"in schemas: {schemas_to_unify}"
                          )
          # Build the concatenated table.
          table = pyarrow.Table.from_arrays(cols, schema=schema)
          # Validate table schema (this is a cheap check by default).
          table.validate()
      else:
          # No extension array columns, so use built-in pyarrow.concat_tables.
          table = pyarrow.concat_tables(blocks, promote=True)
      return table
  
file_path: ray/python/ray/data/_internal/arrow_ops/transform_pyarrow.py
imports: from typing import TYPE_CHECKING, List, Union
label_desc: Use image node_id 5 for calling a global function with example usage: concat(blocks) and returns: table, blocks, blocks