pyo3_arrow/
datatypes.rs

1use std::fmt::Display;
2use std::hash::{DefaultHasher, Hash, Hasher};
3use std::sync::Arc;
4
5use arrow_schema::{DataType, Field, IntervalUnit, TimeUnit};
6use pyo3::exceptions::{PyTypeError, PyValueError};
7use pyo3::intern;
8use pyo3::prelude::*;
9use pyo3::types::{PyCapsule, PyTuple, PyType};
10
11use crate::error::PyArrowResult;
12use crate::export::{Arro3DataType, Arro3Field};
13use crate::ffi::from_python::utils::import_schema_pycapsule;
14use crate::ffi::to_python::nanoarrow::to_nanoarrow_schema;
15use crate::ffi::to_schema_pycapsule;
16use crate::PyField;
17
18struct PyTimeUnit(arrow_schema::TimeUnit);
19
20impl<'a> FromPyObject<'_, 'a> for PyTimeUnit {
21    type Error = PyErr;
22
23    fn extract(obj: Borrowed<'_, 'a, PyAny>) -> Result<Self, Self::Error> {
24        let s: String = obj.extract()?;
25        match s.to_lowercase().as_str() {
26            "s" => Ok(Self(TimeUnit::Second)),
27            "ms" => Ok(Self(TimeUnit::Millisecond)),
28            "us" => Ok(Self(TimeUnit::Microsecond)),
29            "ns" => Ok(Self(TimeUnit::Nanosecond)),
30            _ => Err(PyValueError::new_err("Unexpected time unit")),
31        }
32    }
33}
34
35/// A Python-facing wrapper around [DataType].
36#[derive(PartialEq, Eq, Debug)]
37#[pyclass(module = "arro3.core._core", name = "DataType", subclass, frozen)]
38pub struct PyDataType(DataType);
39
40impl PyDataType {
41    /// Construct a new PyDataType around a [DataType].
42    pub fn new(data_type: DataType) -> Self {
43        Self(data_type)
44    }
45
46    /// Create from a raw Arrow C Schema capsule
47    pub fn from_arrow_pycapsule(capsule: &Bound<PyCapsule>) -> PyResult<Self> {
48        let schema_ptr = import_schema_pycapsule(capsule)?;
49        let data_type =
50            DataType::try_from(schema_ptr).map_err(|err| PyTypeError::new_err(err.to_string()))?;
51        Ok(Self::new(data_type))
52    }
53
54    /// Consume this and return its inner part.
55    pub fn into_inner(self) -> DataType {
56        self.0
57    }
58
59    /// Export this to a Python `arro3.core.DataType`.
60    pub fn to_arro3<'py>(&'py self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
61        let arro3_mod = py.import(intern!(py, "arro3.core"))?;
62        arro3_mod.getattr(intern!(py, "DataType"))?.call_method1(
63            intern!(py, "from_arrow_pycapsule"),
64            PyTuple::new(py, vec![self.__arrow_c_schema__(py)?])?,
65        )
66    }
67
68    /// Export this to a Python `arro3.core.DataType`.
69    pub fn into_arro3(self, py: Python) -> PyResult<Bound<PyAny>> {
70        let arro3_mod = py.import(intern!(py, "arro3.core"))?;
71        let capsule = to_schema_pycapsule(py, &self.0)?;
72        arro3_mod.getattr(intern!(py, "DataType"))?.call_method1(
73            intern!(py, "from_arrow_pycapsule"),
74            PyTuple::new(py, vec![capsule])?,
75        )
76    }
77
78    /// Export this to a Python `nanoarrow.Schema`.
79    pub fn to_nanoarrow<'py>(&'py self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
80        to_nanoarrow_schema(py, &self.__arrow_c_schema__(py)?)
81    }
82
83    /// Export to a pyarrow.DataType
84    ///
85    /// Requires pyarrow >=14
86    pub fn into_pyarrow(self, py: Python) -> PyResult<Bound<PyAny>> {
87        let pyarrow_mod = py.import(intern!(py, "pyarrow"))?;
88        let pyarrow_field = pyarrow_mod
89            .getattr(intern!(py, "field"))?
90            .call1(PyTuple::new(py, vec![self.into_pyobject(py)?])?)?;
91        pyarrow_field.getattr(intern!(py, "type"))
92    }
93}
94
95impl From<PyDataType> for DataType {
96    fn from(value: PyDataType) -> Self {
97        value.0
98    }
99}
100
101impl From<DataType> for PyDataType {
102    fn from(value: DataType) -> Self {
103        Self(value)
104    }
105}
106
107impl From<&DataType> for PyDataType {
108    fn from(value: &DataType) -> Self {
109        Self(value.clone())
110    }
111}
112
113impl AsRef<DataType> for PyDataType {
114    fn as_ref(&self) -> &DataType {
115        &self.0
116    }
117}
118
119impl Display for PyDataType {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        write!(f, "arro3.core.DataType<")?;
122        self.0.fmt(f)?;
123        writeln!(f, ">")?;
124        Ok(())
125    }
126}
127
128#[allow(non_snake_case)]
129#[pymethods]
130impl PyDataType {
131    pub(crate) fn __arrow_c_schema__<'py>(
132        &'py self,
133        py: Python<'py>,
134    ) -> PyArrowResult<Bound<'py, PyCapsule>> {
135        to_schema_pycapsule(py, &self.0)
136    }
137
138    fn __eq__(&self, other: PyDataType) -> bool {
139        self.equals(other, false)
140    }
141
142    fn __hash__(&self) -> u64 {
143        let mut hasher = DefaultHasher::new();
144        self.0.hash(&mut hasher);
145        hasher.finish()
146    }
147
148    fn __repr__(&self) -> String {
149        self.to_string()
150    }
151
152    #[classmethod]
153    fn from_arrow(_cls: &Bound<PyType>, input: Self) -> Self {
154        input
155    }
156
157    #[classmethod]
158    #[pyo3(name = "from_arrow_pycapsule")]
159    fn from_arrow_pycapsule_py(_cls: &Bound<PyType>, capsule: &Bound<PyCapsule>) -> PyResult<Self> {
160        Self::from_arrow_pycapsule(capsule)
161    }
162
163    #[getter]
164    fn bit_width(&self) -> Option<usize> {
165        self.0.primitive_width().map(|width| width * 8)
166    }
167
168    #[pyo3(signature=(other, *, check_metadata=false))]
169    fn equals(&self, other: PyDataType, check_metadata: bool) -> bool {
170        let other = other.into_inner();
171        if check_metadata {
172            self.0 == other
173        } else {
174            self.0.equals_datatype(&other)
175        }
176    }
177
178    #[getter]
179    fn list_size(&self) -> Option<i32> {
180        match &self.0 {
181            DataType::FixedSizeList(_, list_size) => Some(*list_size),
182            _ => None,
183        }
184    }
185
186    #[getter]
187    fn num_fields(&self) -> usize {
188        match &self.0 {
189            DataType::Null
190            | DataType::Boolean
191            | DataType::Int8
192            | DataType::Int16
193            | DataType::Int32
194            | DataType::Int64
195            | DataType::UInt8
196            | DataType::UInt16
197            | DataType::UInt32
198            | DataType::UInt64
199            | DataType::Float16
200            | DataType::Float32
201            | DataType::Float64
202            | DataType::Timestamp(_, _)
203            | DataType::Date32
204            | DataType::Date64
205            | DataType::Time32(_)
206            | DataType::Time64(_)
207            | DataType::Duration(_)
208            | DataType::Interval(_)
209            | DataType::Binary
210            | DataType::FixedSizeBinary(_)
211            | DataType::LargeBinary
212            | DataType::BinaryView
213            | DataType::Utf8
214            | DataType::LargeUtf8
215            | DataType::Utf8View
216            | DataType::Decimal32(_, _)
217            | DataType::Decimal64(_, _)
218            | DataType::Decimal128(_, _)
219            | DataType::Decimal256(_, _) => 0,
220            DataType::List(_)
221            | DataType::ListView(_)
222            | DataType::FixedSizeList(_, _)
223            | DataType::LargeList(_)
224            | DataType::LargeListView(_) => 1,
225            DataType::Struct(fields) => fields.len(),
226            DataType::Union(fields, _) => fields.len(),
227            // Is this accurate?
228            DataType::Dictionary(_, _) | DataType::Map(_, _) | DataType::RunEndEncoded(_, _) => 2,
229        }
230    }
231
232    #[getter]
233    fn time_unit(&self) -> Option<&str> {
234        match &self.0 {
235            DataType::Time32(unit)
236            | DataType::Time64(unit)
237            | DataType::Timestamp(unit, _)
238            | DataType::Duration(unit) => match unit {
239                TimeUnit::Second => Some("s"),
240                TimeUnit::Millisecond => Some("ms"),
241                TimeUnit::Microsecond => Some("us"),
242                TimeUnit::Nanosecond => Some("ns"),
243            },
244            _ => None,
245        }
246    }
247
248    #[getter]
249    fn tz(&self) -> Option<&str> {
250        match &self.0 {
251            DataType::Timestamp(_, tz) => tz.as_deref(),
252            _ => None,
253        }
254    }
255
256    #[getter]
257    fn value_type(&self) -> Option<Arro3DataType> {
258        match &self.0 {
259            DataType::FixedSizeList(value_field, _)
260            | DataType::List(value_field)
261            | DataType::LargeList(value_field)
262            | DataType::ListView(value_field)
263            | DataType::LargeListView(value_field)
264            | DataType::RunEndEncoded(_, value_field) => {
265                Some(PyDataType::new(value_field.data_type().clone()).into())
266            }
267            DataType::Dictionary(_key_type, value_type) => {
268                Some(PyDataType::new(*value_type.clone()).into())
269            }
270            _ => None,
271        }
272    }
273
274    #[getter]
275    fn value_field(&self) -> Option<Arro3Field> {
276        match &self.0 {
277            DataType::FixedSizeList(value_field, _)
278            | DataType::List(value_field)
279            | DataType::LargeList(value_field)
280            | DataType::ListView(value_field)
281            | DataType::LargeListView(value_field) => {
282                Some(PyField::new(value_field.clone()).into())
283            }
284            _ => None,
285        }
286    }
287
288    #[getter]
289    fn fields(&self) -> Option<Vec<Arro3Field>> {
290        match &self.0 {
291            DataType::Struct(fields) => Some(
292                fields
293                    .into_iter()
294                    .map(|f| PyField::new(f.clone()).into())
295                    .collect::<Vec<_>>(),
296            ),
297            _ => None,
298        }
299    }
300
301    ///////////////////// Constructors
302
303    #[classmethod]
304    fn null(_: &Bound<PyType>) -> Self {
305        Self(DataType::Null)
306    }
307
308    #[classmethod]
309    fn bool(_: &Bound<PyType>) -> Self {
310        Self(DataType::Boolean)
311    }
312
313    #[classmethod]
314    fn int8(_: &Bound<PyType>) -> Self {
315        Self(DataType::Int8)
316    }
317
318    #[classmethod]
319    fn int16(_: &Bound<PyType>) -> Self {
320        Self(DataType::Int16)
321    }
322
323    #[classmethod]
324    fn int32(_: &Bound<PyType>) -> Self {
325        Self(DataType::Int32)
326    }
327
328    #[classmethod]
329    fn int64(_: &Bound<PyType>) -> Self {
330        Self(DataType::Int64)
331    }
332
333    #[classmethod]
334    fn uint8(_: &Bound<PyType>) -> Self {
335        Self(DataType::UInt8)
336    }
337
338    #[classmethod]
339    fn uint16(_: &Bound<PyType>) -> Self {
340        Self(DataType::UInt16)
341    }
342
343    #[classmethod]
344    fn uint32(_: &Bound<PyType>) -> Self {
345        Self(DataType::UInt32)
346    }
347
348    #[classmethod]
349    fn uint64(_: &Bound<PyType>) -> Self {
350        Self(DataType::UInt64)
351    }
352
353    #[classmethod]
354    fn float16(_: &Bound<PyType>) -> Self {
355        Self(DataType::Float16)
356    }
357
358    #[classmethod]
359    fn float32(_: &Bound<PyType>) -> Self {
360        Self(DataType::Float32)
361    }
362
363    #[classmethod]
364    fn float64(_: &Bound<PyType>) -> Self {
365        Self(DataType::Float64)
366    }
367
368    #[classmethod]
369    fn time32(_: &Bound<PyType>, unit: PyTimeUnit) -> PyArrowResult<Self> {
370        if unit.0 == TimeUnit::Microsecond || unit.0 == TimeUnit::Nanosecond {
371            return Err(PyValueError::new_err("Unexpected timeunit for time32").into());
372        }
373
374        Ok(Self(DataType::Time32(unit.0)))
375    }
376
377    #[classmethod]
378    fn time64(_: &Bound<PyType>, unit: PyTimeUnit) -> PyArrowResult<Self> {
379        if unit.0 == TimeUnit::Second || unit.0 == TimeUnit::Millisecond {
380            return Err(PyValueError::new_err("Unexpected timeunit for time64").into());
381        }
382
383        Ok(Self(DataType::Time64(unit.0)))
384    }
385
386    #[classmethod]
387    #[pyo3(signature = (unit, *, tz=None))]
388    fn timestamp(_: &Bound<PyType>, unit: PyTimeUnit, tz: Option<String>) -> Self {
389        Self(DataType::Timestamp(unit.0, tz.map(|s| s.into())))
390    }
391
392    #[classmethod]
393    fn date32(_: &Bound<PyType>) -> Self {
394        Self(DataType::Date32)
395    }
396
397    #[classmethod]
398    fn date64(_: &Bound<PyType>) -> Self {
399        Self(DataType::Date64)
400    }
401
402    #[classmethod]
403    fn duration(_: &Bound<PyType>, unit: PyTimeUnit) -> Self {
404        Self(DataType::Duration(unit.0))
405    }
406
407    #[classmethod]
408    fn month_day_nano_interval(_: &Bound<PyType>) -> Self {
409        Self(DataType::Interval(IntervalUnit::MonthDayNano))
410    }
411
412    #[classmethod]
413    #[pyo3(signature = (length=None))]
414    fn binary(_: &Bound<PyType>, length: Option<i32>) -> Self {
415        if let Some(length) = length {
416            Self(DataType::FixedSizeBinary(length))
417        } else {
418            Self(DataType::Binary)
419        }
420    }
421
422    #[classmethod]
423    fn string(_: &Bound<PyType>) -> Self {
424        Self(DataType::Utf8)
425    }
426
427    #[classmethod]
428    fn utf8(_: &Bound<PyType>) -> Self {
429        Self(DataType::Utf8)
430    }
431
432    #[classmethod]
433    fn large_binary(_: &Bound<PyType>) -> Self {
434        Self(DataType::LargeBinary)
435    }
436
437    #[classmethod]
438    fn large_string(_: &Bound<PyType>) -> Self {
439        Self(DataType::LargeUtf8)
440    }
441
442    #[classmethod]
443    fn large_utf8(_: &Bound<PyType>) -> Self {
444        Self(DataType::LargeUtf8)
445    }
446
447    #[classmethod]
448    fn binary_view(_: &Bound<PyType>) -> Self {
449        Self(DataType::BinaryView)
450    }
451
452    #[classmethod]
453    fn string_view(_: &Bound<PyType>) -> Self {
454        Self(DataType::Utf8View)
455    }
456
457    #[classmethod]
458    fn decimal128(_: &Bound<PyType>, precision: u8, scale: i8) -> Self {
459        Self(DataType::Decimal128(precision, scale))
460    }
461
462    #[classmethod]
463    fn decimal256(_: &Bound<PyType>, precision: u8, scale: i8) -> Self {
464        Self(DataType::Decimal256(precision, scale))
465    }
466
467    #[classmethod]
468    #[pyo3(signature = (value_type, list_size=None))]
469    fn list(_: &Bound<PyType>, value_type: PyField, list_size: Option<i32>) -> Self {
470        if let Some(list_size) = list_size {
471            Self(DataType::FixedSizeList(value_type.into(), list_size))
472        } else {
473            Self(DataType::List(value_type.into()))
474        }
475    }
476
477    #[classmethod]
478    fn large_list(_: &Bound<PyType>, value_type: PyField) -> Self {
479        Self(DataType::LargeList(value_type.into()))
480    }
481
482    #[classmethod]
483    fn list_view(_: &Bound<PyType>, value_type: PyField) -> Self {
484        Self(DataType::ListView(value_type.into()))
485    }
486
487    #[classmethod]
488    fn large_list_view(_: &Bound<PyType>, value_type: PyField) -> Self {
489        Self(DataType::LargeListView(value_type.into()))
490    }
491
492    #[classmethod]
493    fn map(_: &Bound<PyType>, key_type: PyField, item_type: PyField, keys_sorted: bool) -> Self {
494        // Note: copied from source of `Field::new_map`
495        // https://github.com/apache/arrow-rs/blob/bf9ce475df82d362631099d491d3454d64d50217/arrow-schema/src/field.rs#L251-L258
496        let data_type = DataType::Map(
497            Arc::new(Field::new(
498                "entries",
499                DataType::Struct(vec![key_type.into_inner(), item_type.into_inner()].into()),
500                false, // The inner map field is always non-nullable (arrow-rs #1697),
501            )),
502            keys_sorted,
503        );
504        Self(data_type)
505    }
506
507    #[classmethod]
508    fn r#struct(_: &Bound<PyType>, fields: Vec<PyField>) -> Self {
509        Self(DataType::Struct(
510            fields.into_iter().map(|field| field.into_inner()).collect(),
511        ))
512    }
513
514    #[classmethod]
515    fn dictionary(_: &Bound<PyType>, index_type: PyDataType, value_type: PyDataType) -> Self {
516        Self(DataType::Dictionary(
517            Box::new(index_type.into_inner()),
518            Box::new(value_type.into_inner()),
519        ))
520    }
521
522    #[classmethod]
523    fn run_end_encoded(_: &Bound<PyType>, run_end_type: PyField, value_type: PyField) -> Self {
524        Self(DataType::RunEndEncoded(
525            run_end_type.into_inner(),
526            value_type.into_inner(),
527        ))
528    }
529
530    ///////////////////// Type checking
531
532    #[staticmethod]
533    fn is_boolean(t: PyDataType) -> bool {
534        t.0 == DataType::Boolean
535    }
536
537    #[staticmethod]
538    fn is_integer(t: PyDataType) -> bool {
539        t.0.is_integer()
540    }
541
542    #[staticmethod]
543    fn is_signed_integer(t: PyDataType) -> bool {
544        t.0.is_signed_integer()
545    }
546
547    #[staticmethod]
548    fn is_unsigned_integer(t: PyDataType) -> bool {
549        t.0.is_unsigned_integer()
550    }
551
552    #[staticmethod]
553    fn is_int8(t: PyDataType) -> bool {
554        t.0 == DataType::Int8
555    }
556    #[staticmethod]
557    fn is_int16(t: PyDataType) -> bool {
558        t.0 == DataType::Int16
559    }
560    #[staticmethod]
561    fn is_int32(t: PyDataType) -> bool {
562        t.0 == DataType::Int32
563    }
564    #[staticmethod]
565    fn is_int64(t: PyDataType) -> bool {
566        t.0 == DataType::Int64
567    }
568    #[staticmethod]
569    fn is_uint8(t: PyDataType) -> bool {
570        t.0 == DataType::UInt8
571    }
572    #[staticmethod]
573    fn is_uint16(t: PyDataType) -> bool {
574        t.0 == DataType::UInt16
575    }
576    #[staticmethod]
577    fn is_uint32(t: PyDataType) -> bool {
578        t.0 == DataType::UInt32
579    }
580    #[staticmethod]
581    fn is_uint64(t: PyDataType) -> bool {
582        t.0 == DataType::UInt64
583    }
584    #[staticmethod]
585    fn is_floating(t: PyDataType) -> bool {
586        t.0.is_floating()
587    }
588    #[staticmethod]
589    fn is_float16(t: PyDataType) -> bool {
590        t.0 == DataType::Float16
591    }
592    #[staticmethod]
593    fn is_float32(t: PyDataType) -> bool {
594        t.0 == DataType::Float32
595    }
596    #[staticmethod]
597    fn is_float64(t: PyDataType) -> bool {
598        t.0 == DataType::Float64
599    }
600    #[staticmethod]
601    fn is_decimal(t: PyDataType) -> bool {
602        matches!(t.0, DataType::Decimal128(_, _) | DataType::Decimal256(_, _))
603    }
604    #[staticmethod]
605    fn is_decimal128(t: PyDataType) -> bool {
606        matches!(t.0, DataType::Decimal128(_, _))
607    }
608    #[staticmethod]
609    fn is_decimal256(t: PyDataType) -> bool {
610        matches!(t.0, DataType::Decimal256(_, _))
611    }
612
613    #[staticmethod]
614    fn is_list(t: PyDataType) -> bool {
615        matches!(t.0, DataType::List(_))
616    }
617    #[staticmethod]
618    fn is_large_list(t: PyDataType) -> bool {
619        matches!(t.0, DataType::LargeList(_))
620    }
621    #[staticmethod]
622    fn is_fixed_size_list(t: PyDataType) -> bool {
623        matches!(t.0, DataType::FixedSizeList(_, _))
624    }
625    #[staticmethod]
626    fn is_list_view(t: PyDataType) -> bool {
627        matches!(t.0, DataType::ListView(_))
628    }
629    #[staticmethod]
630    fn is_large_list_view(t: PyDataType) -> bool {
631        matches!(t.0, DataType::LargeListView(_))
632    }
633    #[staticmethod]
634    fn is_struct(t: PyDataType) -> bool {
635        matches!(t.0, DataType::Struct(_))
636    }
637    #[staticmethod]
638    fn is_union(t: PyDataType) -> bool {
639        matches!(t.0, DataType::Union(_, _))
640    }
641    #[staticmethod]
642    fn is_nested(t: PyDataType) -> bool {
643        t.0.is_nested()
644    }
645    #[staticmethod]
646    fn is_run_end_encoded(t: PyDataType) -> bool {
647        t.0.is_run_ends_type()
648    }
649    #[staticmethod]
650    fn is_temporal(t: PyDataType) -> bool {
651        t.0.is_temporal()
652    }
653    #[staticmethod]
654    fn is_timestamp(t: PyDataType) -> bool {
655        matches!(t.0, DataType::Timestamp(_, _))
656    }
657    #[staticmethod]
658    fn is_date(t: PyDataType) -> bool {
659        matches!(t.0, DataType::Date32 | DataType::Date64)
660    }
661    #[staticmethod]
662    fn is_date32(t: PyDataType) -> bool {
663        t.0 == DataType::Date32
664    }
665    #[staticmethod]
666    fn is_date64(t: PyDataType) -> bool {
667        t.0 == DataType::Date64
668    }
669    #[staticmethod]
670    fn is_time(t: PyDataType) -> bool {
671        matches!(t.0, DataType::Time32(_) | DataType::Time64(_))
672    }
673    #[staticmethod]
674    fn is_time32(t: PyDataType) -> bool {
675        matches!(t.0, DataType::Time32(_))
676    }
677    #[staticmethod]
678    fn is_time64(t: PyDataType) -> bool {
679        matches!(t.0, DataType::Time64(_))
680    }
681    #[staticmethod]
682    fn is_duration(t: PyDataType) -> bool {
683        matches!(t.0, DataType::Duration(_))
684    }
685    #[staticmethod]
686    fn is_interval(t: PyDataType) -> bool {
687        matches!(t.0, DataType::Interval(_))
688    }
689    #[staticmethod]
690    fn is_null(t: PyDataType) -> bool {
691        t.0 == DataType::Null
692    }
693    #[staticmethod]
694    fn is_binary(t: PyDataType) -> bool {
695        t.0 == DataType::Binary
696    }
697    #[staticmethod]
698    fn is_unicode(t: PyDataType) -> bool {
699        t.0 == DataType::Utf8
700    }
701    #[staticmethod]
702    fn is_string(t: PyDataType) -> bool {
703        t.0 == DataType::Utf8
704    }
705    #[staticmethod]
706    fn is_large_binary(t: PyDataType) -> bool {
707        t.0 == DataType::LargeBinary
708    }
709    #[staticmethod]
710    fn is_large_unicode(t: PyDataType) -> bool {
711        t.0 == DataType::LargeUtf8
712    }
713    #[staticmethod]
714    fn is_large_string(t: PyDataType) -> bool {
715        t.0 == DataType::LargeUtf8
716    }
717    #[staticmethod]
718    fn is_binary_view(t: PyDataType) -> bool {
719        t.0 == DataType::BinaryView
720    }
721    #[staticmethod]
722    fn is_string_view(t: PyDataType) -> bool {
723        t.0 == DataType::Utf8View
724    }
725    #[staticmethod]
726    fn is_fixed_size_binary(t: PyDataType) -> bool {
727        matches!(t.0, DataType::FixedSizeBinary(_))
728    }
729    #[staticmethod]
730    fn is_map(t: PyDataType) -> bool {
731        matches!(t.0, DataType::Map(_, _))
732    }
733    #[staticmethod]
734    fn is_dictionary(t: PyDataType) -> bool {
735        matches!(t.0, DataType::Dictionary(_, _))
736    }
737    #[staticmethod]
738    fn is_primitive(t: PyDataType) -> bool {
739        t.0.is_primitive()
740    }
741    #[staticmethod]
742    fn is_numeric(t: PyDataType) -> bool {
743        t.0.is_numeric()
744    }
745    #[staticmethod]
746    fn is_dictionary_key_type(t: PyDataType) -> bool {
747        t.0.is_dictionary_key_type()
748    }
749}