ronantakizawa/codereview-bench huggingface dataset:
Repository: pola-rs/polars
File: crates/polars-stream/src/nodes/io_sinks/config.rs (Rust)

Code before:
            const { NonZeroUsize::new(512).unwrap() }
        } else {
            const { NonZeroUsize::new(128).unwrap() }
        }
    }

    pub fn cloud_upload_chunk_size(&self) -> usize {
        polars_io::get_upload_chunk_size()
    }

    pub fn partitioned_cloud_upload_chunk_size(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE: {x}")
                })
                .get()
        }) {
            return v;
        }

        6 * 1024 * 1024
    }

    pub fn cloud_upload_max_concurrency(&self) -> usize {
        polars_io::get_upload_max_concurrency()
    }

    pub fn partitioned_cloud_upload_max_concurrency(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_MAX_CONCURRENCY").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_MAX_CONCURRENCY: {x}")
                })
                .get()
        }) {
            return v;
        }

        // For now, same default as the underlying object_store::BufWriter default.
        8
    }
}

pub enum IOSinkTarget {
    File(SinkTarget),
    Partitioned(Box<PartitionedTarget>),
}


Review comment (suggestion):
nit -
```suggestion
    pub fn upload_concurrency(&self) -> usize {
```

Code after:
            const { NonZeroUsize::new(512).unwrap() }
        } else {
            const { NonZeroUsize::new(128).unwrap() }
        }
    }

    pub fn cloud_upload_chunk_size(&self) -> usize {
        polars_io::get_upload_chunk_size()
    }

    pub fn partitioned_cloud_upload_chunk_size(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE: {x}")
                })
                .get()
        }) {
            return v;
        }

        6 * 1024 * 1024
    }

    pub fn upload_concurrency(&self) -> usize {
        polars_io::get_upload_concurrency()
    }

    pub fn partitioned_upload_concurrency(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CONCURRENCY").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CONCURRENCY: {x}")
                })
                .get()
        }) {
            return v;
        }

        // For now, same default as the underlying object_store::BufWriter default.
        8
    }
}

pub enum IOSinkTarget {
    File(SinkTarget),
    Partitioned(Box<PartitionedTarget>),
}
Repository: pola-rs/polars
File: crates/polars-stream/src/nodes/io_sinks/config.rs (Rust)

Code before:
    }

    pub fn cloud_upload_chunk_size(&self) -> usize {
        polars_io::get_upload_chunk_size()
    }

    pub fn partitioned_cloud_upload_chunk_size(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE: {x}")
                })
                .get()
        }) {
            return v;
        }

        6 * 1024 * 1024
    }

    pub fn cloud_upload_max_concurrency(&self) -> usize {
        polars_io::get_upload_max_concurrency()
    }

    pub fn partitioned_cloud_upload_max_concurrency(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_MAX_CONCURRENCY").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_MAX_CONCURRENCY: {x}")
                })
                .get()
        }) {
            return v;
        }

        // For now, same default as the underlying object_store::BufWriter default.
        8
    }
}

pub enum IOSinkTarget {
    File(SinkTarget),
    Partitioned(Box<PartitionedTarget>),
}

impl IOSinkTarget {
    pub fn is_cloud_location(&self) -> bool {
        match self {
            Self::File(v) => v.cloud_scheme(),

Review comment (suggestion):
```suggestion
    pub fn partitioned_upload_concurrency(&self) -> usize {
```

Code after:
    }

    pub fn cloud_upload_chunk_size(&self) -> usize {
        polars_io::get_upload_chunk_size()
    }

    pub fn partitioned_cloud_upload_chunk_size(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CHUNK_SIZE: {x}")
                })
                .get()
        }) {
            return v;
        }

        6 * 1024 * 1024
    }

    pub fn upload_concurrency(&self) -> usize {
        polars_io::get_upload_concurrency()
    }

    pub fn partitioned_upload_concurrency(&self) -> usize {
        if let Ok(v) = std::env::var("POLARS_PARTITIONED_UPLOAD_CONCURRENCY").map(|x| {
            x.parse::<NonZeroUsize>()
                .ok()
                .unwrap_or_else(|| {
                    panic!("invalid value for POLARS_PARTITIONED_UPLOAD_CONCURRENCY: {x}")
                })
                .get()
        }) {
            return v;
        }

        // For now, same default as the underlying object_store::BufWriter default.
        8
    }
}

pub enum IOSinkTarget {
    File(SinkTarget),
    Partitioned(Box<PartitionedTarget>),
}

impl IOSinkTarget {
    pub fn is_cloud_location(&self) -> bool {
        match self {
            Self::File(v) => v.cloud_scheme(),
Repository: pola-rs/polars
File: py-polars/tests/unit/expr/test_literal.py (Python)

Code before:

@pytest.mark.parametrize(
    ("numerator", "divisor", "floordiv", "mod"),
    [
        (10, 3, 3, 1),
        (10, 2, 5, 0),
        (1, 2, 0, 1),
        (0, 10, 0, 0),
        (1, 0, None, None),
    ],
)
@pytest.mark.parametrize(
    ("numerator_dtype", "divisor_dtype"),
    [
        (x, y)
        for x in [pl.Int8, pl.UInt8, None]
        for y in [pl.Int8, pl.UInt8, None]
        if x == y or x is None or y is None
    ],
)
def test_floordiv_mod(
    numerator: int,
    divisor: int,
    floordiv: int | None,
    mod: int | None,
    numerator_dtype: pl.Dtype,
    divisor_dtype: pl.Dtype,
) -> None:
    assert_frame_equal(
        pl.select(
            pl.lit(numerator, dtype=numerator_dtype)
            // pl.lit(divisor, dtype=divisor_dtype)
        ),
        pl.DataFrame(
            {"literal": [floordiv]},
            schema={"literal": numerator_dtype or divisor_dtype or pl.Int32},
        ),
    )
    assert_frame_equal(
        pl.select(
            pl.lit(numerator, dtype=numerator_dtype)
            % pl.lit(divisor, dtype=divisor_dtype)
        ),
        pl.DataFrame(
            {"literal": [mod]},
            schema={"literal": numerator_dtype or divisor_dtype or pl.Int32},
        ),
    )


Review comment (suggestion):
```python
if TYPE_CHECKING:
    from polars._typing import PolarsDataType
```
Then use `numerator_dtype: PolarsDataType`.

Code after:
    assert res.schema == {"colx": pl.Object()}
    assert res["colx"].to_list() == [0, 0, 1, 2, 0, 3, 3, 0]


@pytest.mark.parametrize(
    ("numerator", "divisor", "floordiv", "mod"),
    [
        (10, 3, 3, 1),
        (10, 2, 5, 0),
        (1, 2, 0, 1),
        (0, 10, 0, 0),
        (1, 0, None, None),
    ],
)
@pytest.mark.parametrize(
    ("numerator_dtype", "divisor_dtype"),
    [
        (x, y)
        for x in [pl.Int8, pl.UInt8, None]
        for y in [pl.Int8, pl.UInt8, None]
        if x == y or x is None or y is None
    ],
)
def test_floordiv_mod(
    numerator: int,
    divisor: int,
    floordiv: int | None,
    mod: int | None,
    numerator_dtype: PolarsDataType | None,
    divisor_dtype: PolarsDataType | None,
) -> None:
    assert_frame_equal(
        pl.select(
            pl.lit(numerator, dtype=numerator_dtype)
            // pl.lit(divisor, dtype=divisor_dtype)
        ),
        pl.DataFrame(
            {"literal": [floordiv]},
            schema={"literal": numerator_dtype or divisor_dtype or pl.Int32},
        ),
    )
    assert_frame_equal(
        pl.select(
            pl.lit(numerator, dtype=numerator_dtype)
            % pl.lit(divisor, dtype=divisor_dtype)
        ),
        pl.DataFrame(
            {"literal": [mod]},
            schema={"literal": numerator_dtype or divisor_dtype or pl.Int32},
        ),
    )
Repository: pola-rs/polars
File: crates/polars-ops/src/chunked_array/binary/get.rs (Rust)

Code before:
use polars_core::prelude::*;
use polars_error::{PolarsResult, polars_bail};

fn get_byte(bytes: Option<&[u8]>, idx: Option<i64>, null_on_oob: bool) -> PolarsResult<Option<u8>> {
    let (Some(bytes), Some(idx)) = (bytes, idx) else {
        return Ok(None);
    };

    let len = bytes.len() as i64;
    let idx = if idx >= 0 { idx } else { len + idx };
    if idx < 0 || idx >= len {
        if null_on_oob {
            Ok(None)
        } else {
            polars_bail!(ComputeError: "get index is out of bounds")
        }
    } else {
        Ok(Some(bytes[idx as usize]))
    }
}

fn collect_result(
    iter: impl Iterator<Item = PolarsResult<Option<u8>>>,
    name: PlSmallStr,
) -> PolarsResult<Column> {
    let mut out: UInt8Chunked = iter.collect::<PolarsResult<_>>()?;
    out.rename(name);
    Ok(out.into_column())
}

pub fn bin_get(
    ca: &BinaryChunked,
    index: &Int64Chunked,
    null_on_oob: bool,
) -> PolarsResult<Column> {
    let name = ca.name().clone();
    match index.len() {
        1 => {
            let idx = index.get(0);
            collect_result(
                ca.into_iter().map(move |b| get_byte(b, idx, null_on_oob)),
                name,
            )
        },
        len if len == ca.len() => collect_result(
            ca.into_iter()
                .zip(index)

Review comment (suggestion):
Don't think you need this function. Can just do

```rust
let out: PolarsResult<UInt8Chunked> = match index.len() {
    1 => {
        let idx = index.get(0);
        ca.into_iter().map(move |b| get_byte(b, idx, null_on_oob)).collect()
    },
    ...
};
Ok(out?.with_name(name).into_column())
```

Code after:
use polars_core::prelude::arity::broadcast_try_binary_elementwise;
use polars_core::prelude::*;
use polars_error::{PolarsResult, polars_bail};

fn get_byte(bytes: Option<&[u8]>, idx: Option<i64>, null_on_oob: bool) -> PolarsResult<Option<u8>> {
    let (Some(bytes), Some(idx)) = (bytes, idx) else {
        return Ok(None);
    };

    let len = bytes.len() as i64;
    let idx = if idx >= 0 { idx } else { len + idx };
    if idx < 0 || idx >= len {
        if null_on_oob {
            Ok(None)
        } else {
            polars_bail!(ComputeError: "get index is out of bounds")
        }
    } else {
        Ok(Some(bytes[idx as usize]))
    }
}

pub fn bin_get(
    ca: &BinaryChunked,
    index: &Int64Chunked,
    null_on_oob: bool,
) -> PolarsResult<Column> {
    let out: UInt8Chunked =
        broadcast_try_binary_elementwise(ca, index, |b, idx| get_byte(b, idx, null_on_oob))?;
    Ok(out.into_column())
}
Repository: pola-rs/polars
File: crates/polars-ops/src/chunked_array/binary/get.rs (Rust)

Code before:
use polars_error::{PolarsResult, polars_bail};

fn get_byte(bytes: Option<&[u8]>, idx: Option<i64>, null_on_oob: bool) -> PolarsResult<Option<u8>> {
    let (Some(bytes), Some(idx)) = (bytes, idx) else {
        return Ok(None);
    };

    let len = bytes.len() as i64;
    let idx = if idx >= 0 { idx } else { len + idx };
    if idx < 0 || idx >= len {
        if null_on_oob {
            Ok(None)
        } else {
            polars_bail!(ComputeError: "get index is out of bounds")
        }
    } else {
        Ok(Some(bytes[idx as usize]))
    }
}

fn collect_result(
    iter: impl Iterator<Item = PolarsResult<Option<u8>>>,
    name: PlSmallStr,
) -> PolarsResult<Column> {
    let mut out: UInt8Chunked = iter.collect::<PolarsResult<_>>()?;
    out.rename(name);
    Ok(out.into_column())
}

pub fn bin_get(
    ca: &BinaryChunked,
    index: &Int64Chunked,
    null_on_oob: bool,
) -> PolarsResult<Column> {
    let name = ca.name().clone();
    match index.len() {
        1 => {
            let idx = index.get(0);
            collect_result(
                ca.into_iter().map(move |b| get_byte(b, idx, null_on_oob)),
                name,
            )
        },
        len if len == ca.len() => collect_result(
            ca.into_iter()
                .zip(index)
                .map(|(b, i)| get_byte(b, i, null_on_oob)),
            name,
        ),
        _ if ca.len() == 1 => {
            let bytes = ca.get(0);

Review comment (suggestion):
Actually, can you try to replace this entire function body with

```rust
broadcast_try_binary_elementwise(ca, index, |b, idx| get_byte(b, idx, null_on_oob))
```

Code after:
use polars_core::prelude::*;
use polars_error::{PolarsResult, polars_bail};

fn get_byte(bytes: Option<&[u8]>, idx: Option<i64>, null_on_oob: bool) -> PolarsResult<Option<u8>> {
    let (Some(bytes), Some(idx)) = (bytes, idx) else {
        return Ok(None);
    };

    let len = bytes.len() as i64;
    let idx = if idx >= 0 { idx } else { len + idx };
    if idx < 0 || idx >= len {
        if null_on_oob {
            Ok(None)
        } else {
            polars_bail!(ComputeError: "get index is out of bounds")
        }
    } else {
        Ok(Some(bytes[idx as usize]))
    }
}

pub fn bin_get(
    ca: &BinaryChunked,
    index: &Int64Chunked,
    null_on_oob: bool,
) -> PolarsResult<Column> {
    let out: UInt8Chunked =
        broadcast_try_binary_elementwise(ca, index, |b, idx| get_byte(b, idx, null_on_oob))?;
    Ok(out.into_column())
}
Repository: pola-rs/polars
File: py-polars/tests/unit/io/test_iceberg.py (Python)

Code before:
            .select(pl.len())
            .collect()
            .item()
            == 1 << 32
        )

        return

    f = io.BytesIO()

    pl.DataFrame({"x": 1}).write_parquet(f)

    q = pl.scan_parquet([f.getvalue()], schema={"x": pl.Int64}, _row_count=(1 << 32, 0))

    assert_frame_equal(q.collect(), pl.DataFrame({"x": 1}))

    with pytest.raises(
        pl.exceptions.ComputeError,
        match=r"row count \(4294967296\) exceeded maximum supported of 4294967295.*Consider installing 'polars\[rt64\]'.",
    ):
        q.select(pl.len()).collect()


def test_iceberg_filter_bool_26474() -> None:
    catalog = SqlCatalog(
        "test", uri="sqlite:///:memory:", warehouse="file:///tmp/iceberg_mre"
    )
    catalog.create_namespace("default")
    table = catalog.create_table(
        "default.test",
        IcebergSchema(
            NestedField(1, "id", LongType()),
            NestedField(2, "is_active", BooleanType()),
        ),
    )
    pl.DataFrame({"id": [1, 2, 3], "is_active": [True, False, True]}).write_iceberg(
        table, mode="append"
    )

    assert pl.scan_iceberg(table).filter(pl.col("is_active")).collect().height == 2
    assert pl.scan_iceberg(table).filter(~pl.col("is_active")).collect().height == 1


Review comment (suggestion):
`tmp_path: Path` with `pytest.mark.write_disk` instead of `file:///tmp/`


Code after:
            .select(pl.len())
            .collect()
            .item()
            == 1 << 32
        )

        return

    f = io.BytesIO()

    pl.DataFrame({"x": 1}).write_parquet(f)

    q = pl.scan_parquet([f.getvalue()], schema={"x": pl.Int64}, _row_count=(1 << 32, 0))

    assert_frame_equal(q.collect(), pl.DataFrame({"x": 1}))

    with pytest.raises(
        pl.exceptions.ComputeError,
        match=r"row count \(4294967296\) exceeded maximum supported of 4294967295.*Consider installing 'polars\[rt64\]'.",
    ):
        q.select(pl.len()).collect()


@pytest.mark.write_disk
def test_iceberg_filter_bool_26474(tmp_path: Path) -> None:
    catalog = SqlCatalog(
        "test", uri="sqlite:///:memory:", warehouse=format_file_uri_iceberg(tmp_path)
    )
    catalog.create_namespace("default")
    tbl = catalog.create_table(
        "default.test",
        IcebergSchema(
            NestedField(1, "id", LongType()),
            NestedField(2, "foo", BooleanType()),
        ),
    )

    schema = {"id": pl.Int64, "foo": pl.Boolean}

    dfs = [
        pl.DataFrame({"id": [1], "foo": [True]}, schema=schema),
        pl.DataFrame({"id": [2], "foo": [False]}, schema=schema),
        pl.DataFrame({"id": [3], "foo": [None]}, schema=schema),
    ]

    for df in dfs:
        df.write_iceberg(tbl, mode="append")

    assert sum(1 for _ in tbl.scan().plan_files()) == 3

    dfs_concat = pl.concat(dfs)
Repository: pola-rs/polars
File: py-polars/tests/unit/io/test_iceberg.py (Python)

Code before:
    pl.DataFrame({"x": 1}).write_parquet(f)

    q = pl.scan_parquet([f.getvalue()], schema={"x": pl.Int64}, _row_count=(1 << 32, 0))

    assert_frame_equal(q.collect(), pl.DataFrame({"x": 1}))

    with pytest.raises(
        pl.exceptions.ComputeError,
        match=r"row count \(4294967296\) exceeded maximum supported of 4294967295.*Consider installing 'polars\[rt64\]'.",
    ):
        q.select(pl.len()).collect()


def test_iceberg_filter_bool_26474() -> None:
    catalog = SqlCatalog(
        "test", uri="sqlite:///:memory:", warehouse="file:///tmp/iceberg_mre"
    )
    catalog.create_namespace("default")
    table = catalog.create_table(
        "default.test",
        IcebergSchema(
            NestedField(1, "id", LongType()),
            NestedField(2, "is_active", BooleanType()),
        ),
    )
    pl.DataFrame({"id": [1, 2, 3], "is_active": [True, False, True]}).write_iceberg(
        table, mode="append"
    )

    assert pl.scan_iceberg(table).filter(pl.col("is_active")).collect().height == 2
    assert pl.scan_iceberg(table).filter(~pl.col("is_active")).collect().height == 1


Review comment (suggestion):
Could we make 3 separate appends, each contaning a single value from `True, False, None`.

Then assert that the table has 3 individual data files `sum(1 for _ in tbl.scan().plan_files()) == 3`.

This will ensure the predicate skip will have an observable effect, as pyiceberg's filter exclusion operates on the file level.


Code after:
    pl.DataFrame({"x": 1}).write_parquet(f)

    q = pl.scan_parquet([f.getvalue()], schema={"x": pl.Int64}, _row_count=(1 << 32, 0))

    assert_frame_equal(q.collect(), pl.DataFrame({"x": 1}))

    with pytest.raises(
        pl.exceptions.ComputeError,
        match=r"row count \(4294967296\) exceeded maximum supported of 4294967295.*Consider installing 'polars\[rt64\]'.",
    ):
        q.select(pl.len()).collect()


@pytest.mark.write_disk
def test_iceberg_filter_bool_26474(tmp_path: Path) -> None:
    catalog = SqlCatalog(
        "test", uri="sqlite:///:memory:", warehouse=format_file_uri_iceberg(tmp_path)
    )
    catalog.create_namespace("default")
    tbl = catalog.create_table(
        "default.test",
        IcebergSchema(
            NestedField(1, "id", LongType()),
            NestedField(2, "foo", BooleanType()),
        ),
    )

    schema = {"id": pl.Int64, "foo": pl.Boolean}

    dfs = [
        pl.DataFrame({"id": [1], "foo": [True]}, schema=schema),
        pl.DataFrame({"id": [2], "foo": [False]}, schema=schema),
        pl.DataFrame({"id": [3], "foo": [None]}, schema=schema),
    ]

    for df in dfs:
        df.write_iceberg(tbl, mode="append")

    assert sum(1 for _ in tbl.scan().plan_files()) == 3

    dfs_concat = pl.concat(dfs)

    for predicate in [
        pl.col("foo"),
        ~pl.col("foo"),
        pl.col("foo") & pl.col("foo"),
        pl.col("foo") | pl.col("foo"),
        pl.col("foo") ^ pl.col("foo"),
        pl.col("foo") & ~pl.col("foo"),
        pl.col("foo") | ~pl.col("foo"),
        pl.col("foo") ^ pl.col("foo"),
Repository: pola-rs/polars
File: py-polars/src/polars/datatypes/classes.py (Python)

Code before:
from __future__ import annotations

import contextlib
import enum
from collections import OrderedDict
from collections.abc import Mapping
from datetime import tzinfo
from inspect import isclass
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload

import polars._reexport as pl
import polars.datatypes
import polars.functions as F

with contextlib.suppress(ImportError):  # Module not available when building docs
    import polars._plr as plr
    from polars._plr import PyCategories
    from polars._plr import dtype_str_repr as _dtype_str_repr


import polars.datatypes.classes as pldt

if TYPE_CHECKING:
    from collections.abc import Callable, Iterable, Iterator, Sequence

    from typing_extensions import Self

    from polars import Series
    from polars._typing import (
        CategoricalOrdering,
        PolarsDataType,
        PythonDataType,
        SchemaDict,
        TimeUnit,
    )


T = TypeVar("T")
R = TypeVar("R")


class classinstmethod(Generic[R]):
    """Decorator that allows a method to be called from the class OR instance."""

    def __init__(self, func: Callable[..., R]) -> None:
        self.func = func

    def __get__(self, instance: Any, type_: Any) -> Callable[..., R]:
        if instance is not None:
            return self.func.__get__(instance, type_)
        return self.func.__get__(type_, type_)

Review comment (suggestion):
```suggestion
    import sys
    if sys.version_info >= (3, 11):
        from typing import Self
    else:
        from typing_extensions import Self
```

Code after:
from __future__ import annotations

import contextlib
import enum
from collections import OrderedDict
from collections.abc import Mapping
from datetime import tzinfo
from inspect import isclass
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload

import polars._reexport as pl
import polars.datatypes
import polars.functions as F

with contextlib.suppress(ImportError):  # Module not available when building docs
    import polars._plr as plr
    from polars._plr import PyCategories
    from polars._plr import dtype_str_repr as _dtype_str_repr


import polars.datatypes.classes as pldt

if TYPE_CHECKING:
    import sys
    from collections.abc import Callable, Iterable, Iterator, Sequence

    if sys.version_info >= (3, 11):
        from typing import Self
    else:
        from typing_extensions import Self

    from polars import Series
    from polars._typing import (
        CategoricalOrdering,
        PolarsDataType,
        PythonDataType,
        SchemaDict,
        TimeUnit,
    )


T = TypeVar("T")
R = TypeVar("R")


class classinstmethod(Generic[R]):
    """Decorator that allows a method to be called from the class OR instance."""

    def __init__(self, func: Callable[..., R]) -> None:
        self.func = func