use crate::sampler::{RandomSampler, Sampler, SequentialSampler};
use pyo3::{prelude::*, types::PyIterator};
pub(crate) struct PySampler {
order: Order,
error: Option<PyErr>,
}
enum Order {
Sequential,
Random(RandomSampler),
Python(Py<PyAny>),
}
impl PySampler {
pub(crate) fn sequential() -> Self {
Self::new(Order::Sequential)
}
pub(crate) fn random(sampler: RandomSampler) -> Self {
Self::new(Order::Random(sampler))
}
pub(crate) fn python(py: Python<'_>, sampler: Py<PyAny>) -> PyResult<Self> {
PyIterator::from_object(sampler.bind(py))?;
Ok(Self::new(Order::Python(sampler)))
}
fn new(order: Order) -> Self {
Self { order, error: None }
}
pub(crate) fn python_sampler(&self) -> Option<&Py<PyAny>> {
match &self.order {
Order::Python(sampler) => Some(sampler),
Order::Sequential | Order::Random(_) => None,
}
}
pub(crate) fn take_error(&mut self) -> Option<PyErr> {
self.error.take()
}
}
const SIGNAL_CHECK_EVERY: usize = 1 << 16;
fn python_indices(py: Python<'_>, sampler: &Py<PyAny>) -> PyResult<Vec<usize>> {
let mut indices = Vec::new();
for item in PyIterator::from_object(sampler.bind(py))? {
if indices.len() % SIGNAL_CHECK_EVERY == 0 {
py.check_signals()?;
}
indices.push(item?.extract::<usize>()?);
}
Ok(indices)
}
impl Sampler for PySampler {
fn indices(&mut self, dataset_len: usize) -> Vec<usize> {
match &mut self.order {
Order::Sequential => SequentialSampler.indices(dataset_len),
Order::Random(sampler) => sampler.indices(dataset_len),
Order::Python(sampler) => Python::attach(|py| python_indices(py, sampler))
.unwrap_or_else(|err| {
self.error = Some(err);
Vec::new()
}),
}
}
}