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
11from typing import TYPE_CHECKING
12
13import numpy as np
14
15from pandas._libs.internals import BlockPlacement
16
17from pandas.core.dtypes.common import pandas_dtype
18from pandas.core.dtypes.dtypes import (
19 DatetimeTZDtype,
20 PeriodDtype,
21)
22
23from pandas.core.arrays import DatetimeArray
24from pandas.core.construction import extract_array
25from pandas.core.internals.blocks import (
26 check_ndim,
27 ensure_block_shape,
28 extract_pandas_array,
29 get_block_type,
30 maybe_coerce_values,
31)
32
33if TYPE_CHECKING:
34 from pandas._typing import Dtype
35
36 from pandas.core.internals.blocks import Block
37
38
39def make_block(
40 values, placement, klass=None, ndim=None, dtype: Dtype | None = None
41) -> Block:
42 """
43 This is a pseudo-public analogue to blocks.new_block.
44
45 We ask that downstream libraries use this rather than any fully-internal
46 APIs, including but not limited to:
47
48 - core.internals.blocks.make_block
49 - Block.make_block
50 - Block.make_block_same_class
51 - Block.__init__
52 """
53 if dtype is not None:
54 dtype = pandas_dtype(dtype)
55
56 values, dtype = extract_pandas_array(values, dtype, ndim)
57
58 from pandas.core.internals.blocks import (
59 DatetimeTZBlock,
60 ExtensionBlock,
61 )
62
63 if klass is ExtensionBlock and isinstance(values.dtype, PeriodDtype):
64 # GH-44681 changed PeriodArray to be stored in the 2D
65 # NDArrayBackedExtensionBlock instead of ExtensionBlock
66 # -> still allow ExtensionBlock to be passed in this case for back compat
67 klass = None
68
69 if klass is None:
70 dtype = dtype or values.dtype
71 klass = get_block_type(dtype)
72
73 elif klass is DatetimeTZBlock and not isinstance(values.dtype, DatetimeTZDtype):
74 # pyarrow calls get here
75 values = DatetimeArray._simple_new(
76 # error: Argument "dtype" to "_simple_new" of "DatetimeArray" has
77 # incompatible type "Union[ExtensionDtype, dtype[Any], None]";
78 # expected "Union[dtype[datetime64], DatetimeTZDtype]"
79 values,
80 dtype=dtype, # type: ignore[arg-type]
81 )
82
83 if not isinstance(placement, BlockPlacement):
84 placement = BlockPlacement(placement)
85
86 ndim = maybe_infer_ndim(values, placement, ndim)
87 if isinstance(values.dtype, (PeriodDtype, DatetimeTZDtype)):
88 # GH#41168 ensure we can pass 1D dt64tz values
89 # More generally, any EA dtype that isn't is_1d_only_ea_dtype
90 values = extract_array(values, extract_numpy=True)
91 values = ensure_block_shape(values, ndim)
92
93 check_ndim(values, placement, ndim)
94 values = maybe_coerce_values(values)
95 return klass(values, ndim=ndim, placement=placement)
96
97
98def maybe_infer_ndim(values, placement: BlockPlacement, ndim: int | None) -> int:
99 """
100 If `ndim` is not provided, infer it from placement and values.
101 """
102 if ndim is None:
103 # GH#38134 Block constructor now assumes ndim is not None
104 if not isinstance(values.dtype, np.dtype):
105 if len(placement) != 1:
106 ndim = 1
107 else:
108 ndim = 2
109 else:
110 ndim = values.ndim
111 return ndim
112
113
114def __getattr__(name: str):
115 # GH#55139
116 import warnings
117
118 if name in [
119 "Block",
120 "ExtensionBlock",
121 "DatetimeTZBlock",
122 "create_block_manager_from_blocks",
123 ]:
124 # GH#33892
125 warnings.warn(
126 f"{name} is deprecated and will be removed in a future version. "
127 "Use public APIs instead.",
128 DeprecationWarning,
129 # https://github.com/pandas-dev/pandas/pull/55139#pullrequestreview-1720690758
130 # on hard-coding stacklevel
131 stacklevel=2,
132 )
133
134 if name == "create_block_manager_from_blocks":
135 from pandas.core.internals.managers import create_block_manager_from_blocks
136
137 return create_block_manager_from_blocks
138
139 elif name == "Block":
140 from pandas.core.internals.blocks import Block
141
142 return Block
143
144 elif name == "DatetimeTZBlock":
145 from pandas.core.internals.blocks import DatetimeTZBlock
146
147 return DatetimeTZBlock
148
149 elif name == "ExtensionBlock":
150 from pandas.core.internals.blocks import ExtensionBlock
151
152 return ExtensionBlock
153
154 raise AttributeError(
155 f"module 'pandas.core.internals.api' has no attribute '{name}'"
156 )