def _concat_compat(to_concat, axis=<NUM_LIT:0>):
<EOL>def is_nonempty(x):<EOL><INDENT>try:<EOL><INDENT>return x.shape[axis] > <NUM_LIT:0><EOL><DEDENT>except Exception:<EOL><INDENT>return True<EOL><DEDENT><DEDENT>typs = get_dtype_kinds(to_concat)<EOL>_contains_datetime = any(typ.startswith('<STR_LIT>') for typ in typs)<EOL>_contains_period = any(typ.startswith('<STR_LIT>') for typ in typs)<EOL>if '<STR_LIT>' in typs:<EOL><INDENT>return _concat_categorical(to_concat, axis=axis)<EOL><DEDENT>elif _contains_datetime or '<STR_LIT>' in typs or _contains_period:<EOL><INDENT>return _concat_datetime(to_concat, axis=axis, typs=typs)<EOL><DEDENT>elif '<STR_LIT>' in typs:<EOL><INDENT>return _concat_sparse(to_concat, axis=axis, typs=typs)<EOL><DEDENT>all_empty = all(not is_nonempty(x) for x in to_concat)<EOL>if any(is_extension_array_dtype(x) for x in to_concat) and axis == <NUM_LIT:1>:<EOL><INDENT>to_concat = [np.atleast_2d(x.astype('<STR_LIT:object>')) for x in to_concat]<EOL><DEDENT>if all_empty:<EOL><INDENT>typs = get_dtype_kinds(to_concat)<EOL>if len(typs) != <NUM_LIT:1>:<EOL><INDENT>if (not len(typs - {'<STR_LIT:i>', '<STR_LIT:u>', '<STR_LIT:f>'}) or<EOL>not len(typs - {'<STR_LIT:bool>', '<STR_LIT:i>', '<STR_LIT:u>'})):<EOL><INDENT>pass<EOL><DEDENT>else:<EOL><INDENT>to_concat = [x.astype('<STR_LIT:object>') for x in to_concat]<EOL><DEDENT><DEDENT><DEDENT>return np.concatenate(to_concat, axis=axis)<EOL>
provide concatenation of an array of arrays each of which is a single
'normalized' dtypes (in that for example, if it's object, then it is a
non-datetimelike and provide a combined dtype for the resulting array that
preserves the overall dtype if possible)

Parameters
----------
to_concat : array of arrays
axis : axis to provide concatenation

Returns
-------
a single array, preserving the combined dtypes