Skip to content

Commit 7bb0470

Browse files
committed
refactor(pandas-format): move to classmethods to pickup super class behavior where possible
1 parent a3fac3e commit 7bb0470

File tree

2 files changed

+79
-71
lines changed

2 files changed

+79
-71
lines changed

ibis/backends/snowflake/converter.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,29 @@
1414

1515

1616
class SnowflakePandasData(PandasData):
17-
@staticmethod
18-
def convert_JSON(s, dtype, pandas_type):
19-
converter = SnowflakePandasData.convert_JSON_element(dtype)
17+
@classmethod
18+
def convert_JSON(cls, s, dtype, pandas_type):
19+
converter = cls.convert_JSON_element(dtype)
2020
return s.map(converter, na_action="ignore").astype("object")
2121

2222
convert_Struct = convert_Map = convert_JSON
2323

24-
@staticmethod
25-
def get_element_converter(dtype):
26-
funcgen = getattr(
27-
SnowflakePandasData,
28-
f"convert_{type(dtype).__name__}_element",
29-
lambda _: lambda x: x,
30-
)
31-
return funcgen(dtype)
32-
33-
def convert_Timestamp_element(dtype):
34-
return lambda values: list(map(datetime.datetime.fromisoformat, values))
24+
@classmethod
25+
def convert_Timestamp_element(cls, dtype):
26+
return datetime.datetime.fromisoformat
3527

36-
def convert_Date_element(dtype):
37-
return lambda values: list(map(datetime.date.fromisoformat, values))
28+
@classmethod
29+
def convert_Date_element(cls, dtype):
30+
return datetime.date.fromisoformat
3831

39-
def convert_Time_element(dtype):
40-
return lambda values: list(map(datetime.time.fromisoformat, values))
32+
@classmethod
33+
def convert_Time_element(cls, dtype):
34+
return datetime.time.fromisoformat
4135

42-
@staticmethod
43-
def convert_Array(s, dtype, pandas_type):
44-
raw_json_objects = SnowflakePandasData.convert_JSON(s, dtype, pandas_type)
45-
converter = SnowflakePandasData.get_element_converter(dtype.value_type)
36+
@classmethod
37+
def convert_Array(cls, s, dtype, pandas_type):
38+
raw_json_objects = cls.convert_JSON(s, dtype, pandas_type)
39+
converter = cls.get_element_converter(dtype.value_type)
4640
return raw_json_objects.map(converter, na_action="ignore")
4741

4842

ibis/formats/pandas.py

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import contextlib
34
import json
45
import warnings
56

@@ -10,6 +11,7 @@
1011

1112
import ibis.expr.datatypes as dt
1213
import ibis.expr.schema as sch
14+
from ibis.common.temporal import normalize_timezone
1315
from ibis.formats import DataMapper, SchemaMapper, TableProxy
1416
from ibis.formats.numpy import NumpyType
1517
from ibis.formats.pyarrow import PyArrowData, PyArrowSchema, PyArrowType
@@ -132,8 +134,8 @@ def convert_column(cls, obj, dtype):
132134
assert not isinstance(result, np.ndarray), f"{convert_method} -> {type(result)}"
133135
return result
134136

135-
@staticmethod
136-
def convert_GeoSpatial(s, dtype, pandas_type):
137+
@classmethod
138+
def convert_GeoSpatial(cls, s, dtype, pandas_type):
137139
return s
138140

139141
convert_Point = (
@@ -144,15 +146,15 @@ def convert_GeoSpatial(s, dtype, pandas_type):
144146
convert_MultiLineString
145147
) = convert_MultiPoint = convert_MultiPolygon = convert_GeoSpatial
146148

147-
@staticmethod
148-
def convert_default(s, dtype, pandas_type):
149+
@classmethod
150+
def convert_default(cls, s, dtype, pandas_type):
149151
try:
150152
return s.astype(pandas_type)
151153
except Exception: # noqa: BLE001
152154
return s
153155

154-
@staticmethod
155-
def convert_Boolean(s, dtype, pandas_type):
156+
@classmethod
157+
def convert_Boolean(cls, s, dtype, pandas_type):
156158
if s.empty:
157159
return s.astype(pandas_type)
158160
elif pdt.is_object_dtype(s.dtype):
@@ -162,8 +164,8 @@ def convert_Boolean(s, dtype, pandas_type):
162164
else:
163165
return s
164166

165-
@staticmethod
166-
def convert_Timestamp(s, dtype, pandas_type):
167+
@classmethod
168+
def convert_Timestamp(cls, s, dtype, pandas_type):
167169
if isinstance(dtype, pd.DatetimeTZDtype):
168170
return s.dt.tz_convert(dtype.timezone)
169171
elif pdt.is_datetime64_dtype(s.dtype):
@@ -184,14 +186,14 @@ def convert_Timestamp(s, dtype, pandas_type):
184186
except TypeError:
185187
return pd.to_datetime(s).dt.tz_localize(dtype.timezone)
186188

187-
@staticmethod
188-
def convert_Date(s, dtype, pandas_type):
189+
@classmethod
190+
def convert_Date(cls, s, dtype, pandas_type):
189191
if isinstance(s.dtype, pd.DatetimeTZDtype):
190192
s = s.dt.tz_convert("UTC").dt.tz_localize(None)
191193
return s.astype(pandas_type, errors="ignore").dt.normalize()
192194

193-
@staticmethod
194-
def convert_Interval(s, dtype, pandas_type):
195+
@classmethod
196+
def convert_Interval(cls, s, dtype, pandas_type):
195197
values = s.values
196198
try:
197199
result = values.astype(pandas_type)
@@ -201,42 +203,41 @@ def convert_Interval(s, dtype, pandas_type):
201203
result = s.__class__(result, index=s.index, name=s.name)
202204
return result
203205

204-
@staticmethod
205-
def convert_String(s, dtype, pandas_type):
206+
@classmethod
207+
def convert_String(cls, s, dtype, pandas_type):
206208
return s.astype(pandas_type, errors="ignore")
207209

208-
@staticmethod
209-
def convert_UUID(s, dtype, pandas_type):
210-
return s.map(PandasData.get_element_converter(dtype), na_action="ignore")
211-
212-
@staticmethod
213-
def convert_Struct(s, dtype, pandas_type):
214-
return s.map(PandasData.get_element_converter(dtype), na_action="ignore")
210+
@classmethod
211+
def convert_UUID(cls, s, dtype, pandas_type):
212+
return s.map(cls.get_element_converter(dtype), na_action="ignore")
215213

216-
@staticmethod
217-
def convert_Array(s, dtype, pandas_type):
218-
return s.map(PandasData.get_element_converter(dtype), na_action="ignore")
214+
@classmethod
215+
def convert_Struct(cls, s, dtype, pandas_type):
216+
return s.map(cls.get_element_converter(dtype), na_action="ignore")
219217

220-
@staticmethod
221-
def convert_Map(s, dtype, pandas_type):
222-
return s.map(PandasData.get_element_converter(dtype), na_action="ignore")
218+
@classmethod
219+
def convert_Array(cls, s, dtype, pandas_type):
220+
return s.map(cls.get_element_converter(dtype), na_action="ignore")
223221

224-
@staticmethod
225-
def convert_JSON(s, dtype, pandas_type):
226-
return s.map(
227-
PandasData.get_element_converter(dtype), na_action="ignore"
228-
).astype("object")
222+
@classmethod
223+
def convert_Map(cls, s, dtype, pandas_type):
224+
return s.map(cls.get_element_converter(dtype), na_action="ignore")
229225

230-
@staticmethod
231-
def get_element_converter(dtype):
232-
funcgen = getattr(
233-
PandasData, f"convert_{type(dtype).__name__}_element", lambda _: lambda x: x
226+
@classmethod
227+
def convert_JSON(cls, s, dtype, pandas_type):
228+
return s.map(cls.get_element_converter(dtype), na_action="ignore").astype(
229+
"object"
234230
)
231+
232+
@classmethod
233+
def get_element_converter(cls, dtype):
234+
name = f"convert_{type(dtype).__name__}_element"
235+
funcgen = getattr(cls, name, lambda _: lambda x: x)
235236
return funcgen(dtype)
236237

237-
@staticmethod
238-
def convert_Struct_element(dtype):
239-
converters = tuple(map(PandasData.get_element_converter, dtype.types))
238+
@classmethod
239+
def convert_Struct_element(cls, dtype):
240+
converters = tuple(map(cls.get_element_converter, dtype.types))
240241

241242
def convert(values, names=dtype.names, converters=converters):
242243
items = values.items() if isinstance(values, dict) else zip(names, values)
@@ -247,8 +248,8 @@ def convert(values, names=dtype.names, converters=converters):
247248

248249
return convert
249250

250-
@staticmethod
251-
def convert_JSON_element(_):
251+
@classmethod
252+
def convert_JSON_element(cls, _):
252253
def try_json(x):
253254
if x is None:
254255
return x
@@ -259,23 +260,36 @@ def try_json(x):
259260

260261
return try_json
261262

262-
@staticmethod
263-
def convert_Array_element(dtype):
264-
convert_value = PandasData.get_element_converter(dtype.value_type)
263+
@classmethod
264+
def convert_Timestamp_element(cls, dtype):
265+
def converter(value, dtype=dtype):
266+
with contextlib.suppress(AttributeError):
267+
value = value.item()
268+
269+
if (tz := dtype.timezone) is not None:
270+
return value.astimezone(normalize_timezone(tz))
271+
272+
return value.replace(tzinfo=None)
273+
274+
return converter
275+
276+
@classmethod
277+
def convert_Array_element(cls, dtype):
278+
convert_value = cls.get_element_converter(dtype.value_type)
265279
return lambda values: [
266280
convert_value(value) if value is not None else value for value in values
267281
]
268282

269-
@staticmethod
270-
def convert_Map_element(dtype):
271-
convert_value = PandasData.get_element_converter(dtype.value_type)
283+
@classmethod
284+
def convert_Map_element(cls, dtype):
285+
convert_value = cls.get_element_converter(dtype.value_type)
272286
return lambda row: {
273287
key: convert_value(value) if value is not None else value
274288
for key, value in dict(row).items()
275289
}
276290

277-
@staticmethod
278-
def convert_UUID_element(_):
291+
@classmethod
292+
def convert_UUID_element(cls, _):
279293
from uuid import UUID
280294

281295
return lambda v: v if isinstance(v, UUID) else UUID(v)

0 commit comments

Comments
 (0)