1"""
2This is a pseudo-public API for downstream libraries. We ask that downstream
3authors
4
51) Try to avoid using internals directly altogether, and failing that,
62) Use only functions exposed here (or in core.internals)
7
8"""
9from __future__ import annotations
10
11import numpy as np
12
13from pandas._libs.internals import BlockPlacement
14from pandas._typing import Dtype
15
16from pandas.core.dtypes.common import (
17 is_datetime64tz_dtype,
18 is_period_dtype,
19 pandas_dtype,
20)
21
22from pandas.core.arrays import DatetimeArray
23from pandas.core.construction import extract_array
24from pandas.core.internals.blocks import (
25 Block,
26 DatetimeTZBlock,
27 ExtensionBlock,
28 check_ndim,
29 ensure_block_shape,
30 extract_pandas_array,
31 get_block_type,
32 maybe_coerce_values,
33)
34
35
36def make_block(
37 values, placement, klass=None, ndim=None, dtype: Dtype | None = None
38) -> Block:
39 """
40 This is a pseudo-public analogue to blocks.new_block.
41
42 We ask that downstream libraries use this rather than any fully-internal
43 APIs, including but not limited to:
44
45 - core.internals.blocks.make_block
46 - Block.make_block
47 - Block.make_block_same_class
48 - Block.__init__
49 """
50 if dtype is not None:
51 dtype = pandas_dtype(dtype)
52
53 values, dtype = extract_pandas_array(values, dtype, ndim)
54
55 if klass is ExtensionBlock and is_period_dtype(values.dtype):
56 # GH-44681 changed PeriodArray to be stored in the 2D
57 # NDArrayBackedExtensionBlock instead of ExtensionBlock
58 # -> still allow ExtensionBlock to be passed in this case for back compat
59 klass = None
60
61 if klass is None:
62 dtype = dtype or values.dtype
63 klass = get_block_type(dtype)
64
65 elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype):
66 # pyarrow calls get here
67 values = DatetimeArray._simple_new(values, dtype=dtype)
68
69 if not isinstance(placement, BlockPlacement):
70 placement = BlockPlacement(placement)
71
72 ndim = maybe_infer_ndim(values, placement, ndim)
73 if is_datetime64tz_dtype(values.dtype) or is_period_dtype(values.dtype):
74 # GH#41168 ensure we can pass 1D dt64tz values
75 # More generally, any EA dtype that isn't is_1d_only_ea_dtype
76 values = extract_array(values, extract_numpy=True)
77 values = ensure_block_shape(values, ndim)
78
79 check_ndim(values, placement, ndim)
80 values = maybe_coerce_values(values)
81 return klass(values, ndim=ndim, placement=placement)
82
83
84def maybe_infer_ndim(values, placement: BlockPlacement, ndim: int | None) -> int:
85 """
86 If `ndim` is not provided, infer it from placement and values.
87 """
88 if ndim is None:
89 # GH#38134 Block constructor now assumes ndim is not None
90 if not isinstance(values.dtype, np.dtype):
91 if len(placement) != 1:
92 ndim = 1
93 else:
94 ndim = 2
95 else:
96 ndim = values.ndim
97 return ndim