code: def concatenate_managers(
      mgrs_indexers, axes: list[Index], concat_axis: AxisInt, copy: bool
  ) -> Manager2D:
      """
      Concatenate block managers into one.
  
      Parameters
      ----------
      mgrs_indexers : list of (BlockManager, {axis: indexer,...}) tuples
      axes : list of Index
      concat_axis : int
      copy : bool
  
      Returns
      -------
      BlockManager
      """
  
      needs_copy = copy and concat_axis == 0
  
      # TODO(ArrayManager) this assumes that all managers are of the same type
      if isinstance(mgrs_indexers[0][0], ArrayManager):
          mgrs = _maybe_reindex_columns_na_proxy(
              axes, mgrs_indexers, needs_copy
          )
          # error: Argument 1 to "_concatenate_array_managers" has incompatible
          # type "List[BlockManager]"; expected "List[Union[ArrayManager,
          # SingleArrayManager, BlockManager, SingleBlockManager]]"
          return _concatenate_array_managers(
              mgrs,  # type: ignore[arg-type]
              axes,
              concat_axis,
          )
  
      # Assertions disabled for performance
      # for tup in mgrs_indexers:
      #    # caller is responsible for ensuring this
      #    indexers = tup[1]
      #    assert concat_axis not in indexers
  
      if concat_axis == 0:
          mgrs = _maybe_reindex_columns_na_proxy(
              axes, mgrs_indexers, needs_copy
          )
          return mgrs[0].concat_horizontal(mgrs, axes)
  
      if len(mgrs_indexers) > 0 and mgrs_indexers[0][0].nblocks > 0:
          first_dtype = mgrs_indexers[0][0].blocks[0].dtype
          if first_dtype in [np.float64, np.float32]:
              # TODO: support more dtypes here.  This will be simpler once
              #  JoinUnit.is_na behavior is deprecated.
              if (
                  all(
                      _is_homogeneous_mgr(mgr, first_dtype)
                      for mgr, _ in mgrs_indexers
                  )
                  and len(mgrs_indexers) > 1
              ):
                  # Fastpath!
                  # Length restriction is just to avoid having to worry about 'copy'
                  shape = tuple(len(x) for x in axes)
                  nb = _concat_homogeneous_fastpath(
                      mgrs_indexers, shape, first_dtype
                  )
                  return BlockManager((nb,), axes)
  
      mgrs = _maybe_reindex_columns_na_proxy(
          axes, mgrs_indexers, needs_copy
      )
  
      if len(mgrs) == 1:
          mgr = mgrs[0]
          out = mgr.copy(deep=False)
          out.axes = axes
          return out
  
      concat_plan = _get_combined_plan(mgrs)
  
      blocks = []
      values: ArrayLike
  
      for placement, join_units in concat_plan:
          unit = join_units[0]
          blk = unit.block
  
          if _is_uniform_join_units(join_units):
              vals = [ju.block.values for ju in join_units]
  
              if not blk.is_extension:
                  # _is_uniform_join_units ensures a single dtype, so
                  #  we can use np.concatenate, which is more performant
                  #  than concat_compat
                  # error: Argument 1 to "concatenate" has incompatible type
                  # "List[Union[ndarray[Any, Any], ExtensionArray]]";
                  # expected "Union[_SupportsArray[dtype[Any]],
                  # _NestedSequence[_SupportsArray[dtype[Any]]]]"
                  values = np.concatenate(vals, axis=1)  # type: ignore[arg-type]
              elif is_1d_only_ea_dtype(blk.dtype):
                  # TODO(EA2D): special-casing not needed with 2D EAs
                  values = concat_compat(
                      vals, axis=0, ea_compat_axis=True
                  )
                  values = ensure_block_shape(values, ndim=2)
              else:
                  values = concat_compat(vals, axis=1)
  
              values = ensure_wrapped_if_datetimelike(values)
  
              fastpath = blk.values.dtype == values.dtype
          else:
              values = _concatenate_join_units(join_units, copy=copy)
              fastpath = False
  
          if fastpath:
              b = blk.make_block_same_class(values, placement=placement)
          else:
              b = new_block_2d(values, placement=placement)
  
          blocks.append(b)
  
      return BlockManager(tuple(blocks), axes)
  
imports: from __future__ import annotations
  from typing import TYPE_CHECKING, cast
  import warnings
  import numpy
  from pandas._libs import NaT, algos, internals, lib
  from pandas._libs.missing import NA
  from pandas.util._decorators import cache_readonly
  from pandas.util._exceptions import find_stack_level
  from pandas.core.dtypes.cast import ensure_dtype_can_hold_na, find_common_type
  from pandas.core.dtypes.common import is_1d_only_ea_dtype, is_scalar, needs_i8_conversion
  from pandas.core.dtypes.concat import concat_compat
  from pandas.core.dtypes.dtypes import ExtensionDtype, SparseDtype
  from pandas.core.dtypes.missing import is_valid_na_for_dtype, isna, isna_all
  from pandas.core.construction import ensure_wrapped_if_datetimelike
  from pandas.core.internals.array_manager import ArrayManager
  from pandas.core.internals.blocks import ensure_block_shape, new_block_2d
  from pandas.core.internals.managers import BlockManager, make_na_array
label_desc: Use image node_id 2 for calling a global function with example usage: concatenate_managers(mgrs_indexers, axes, concat_axis, copy) and returns: BlockManager, _concatenate_array_managers, mgrs, out, BlockManager