use pyo3::exceptions::{PyIndexError, PyRuntimeError};
use pyo3::prelude::*;
use pyo3::types::PyList;
use std::sync::{Arc, RwLock};
use lerna::omegaconf::{AnyNode, ConfigValue, ListConfig, Node, NodeValue, OmegaConf};
use super::dictconfig::{node_arc_to_py, py_to_config_value};
#[pyclass(name = "ListConfig")]
#[derive(Debug)]
pub struct PyListConfig {
pub inner: Arc<RwLock<ListConfig>>,
}
#[pymethods]
impl PyListConfig {
#[new]
#[pyo3(signature = (content=None))]
pub fn new(content: Option<&Bound<PyList>>) -> PyResult<Self> {
let cfg = match content {
Some(list) => {
let mut items = Vec::new();
for item in list.iter() {
items.push(py_to_config_value(&item)?);
}
OmegaConf::create_list(items)
}
None => ListConfig::new(),
};
Ok(Self {
inner: Arc::new(RwLock::new(cfg)),
})
}
fn __getitem__(&self, index: isize) -> PyResult<Py<PyAny>> {
Python::attach(|py| {
let cfg = self.inner.read().map_err(|e| {
PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e))
})?;
let len = cfg.len() as isize;
let actual_index = if index < 0 { len + index } else { index };
if actual_index < 0 || actual_index >= len {
return Err(PyIndexError::new_err("list index out of range"));
}
match cfg.get(actual_index as usize) {
Some(node) => node_arc_to_py(node, py),
None => Err(PyIndexError::new_err("list index out of range")),
}
})
}
fn __setitem__(&mut self, index: isize, value: &Bound<PyAny>) -> PyResult<()> {
let config_value = py_to_config_value(value)?;
let node = config_value_to_node(config_value);
let mut cfg = self
.inner
.write()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
let len = cfg.len() as isize;
let actual_index = if index < 0 { len + index } else { index };
if actual_index < 0 || actual_index >= len {
return Err(PyIndexError::new_err("list index out of range"));
}
cfg.set(actual_index as usize, node)
.map_err(|e| PyRuntimeError::new_err(format!("{}", e)))
}
fn __delitem__(&mut self, index: isize) -> PyResult<()> {
let mut cfg = self
.inner
.write()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
let len = cfg.len() as isize;
let actual_index = if index < 0 { len + index } else { index };
if actual_index < 0 || actual_index >= len {
return Err(PyIndexError::new_err("list index out of range"));
}
cfg.remove(actual_index as usize)
.map(|_| ()) .map_err(|e| PyRuntimeError::new_err(format!("{}", e)))
}
fn __len__(&self) -> PyResult<usize> {
let cfg = self
.inner
.read()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
Ok(cfg.len())
}
fn append(&mut self, value: &Bound<PyAny>) -> PyResult<()> {
let config_value = py_to_config_value(value)?;
let node = config_value_to_node(config_value);
let mut cfg = self
.inner
.write()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
cfg.append(node)
.map_err(|e| PyRuntimeError::new_err(format!("{}", e)))
}
fn insert(&mut self, index: usize, value: &Bound<PyAny>) -> PyResult<()> {
let config_value = py_to_config_value(value)?;
let node = config_value_to_node(config_value);
let mut cfg = self
.inner
.write()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
cfg.insert(index, node)
.map_err(|e| PyRuntimeError::new_err(format!("{}", e)))
}
fn pop(&mut self) -> PyResult<Py<PyAny>> {
Python::attach(|py| {
let mut cfg = self.inner.write().map_err(|e| {
PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e))
})?;
match cfg.pop() {
Ok(Some(node)) => node_arc_to_py(node, py),
Ok(None) => Err(PyIndexError::new_err("pop from empty list")),
Err(e) => Err(PyRuntimeError::new_err(format!("{}", e))),
}
})
}
fn clear(&mut self) -> PyResult<()> {
let mut cfg = self
.inner
.write()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
cfg.clear()
.map_err(|e| PyRuntimeError::new_err(format!("{}", e)))
}
fn __repr__(&self) -> PyResult<String> {
let cfg = self
.inner
.read()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to lock ListConfig: {}", e)))?;
Ok(format!("ListConfig([... {} items ...])", cfg.len()))
}
}
fn config_value_to_node(value: ConfigValue) -> Arc<RwLock<dyn Node>> {
match value {
ConfigValue::None => Arc::new(RwLock::new(AnyNode::new(None))),
ConfigValue::Missing => Arc::new(RwLock::new(AnyNode::missing())),
ConfigValue::Bool(v) => Arc::new(RwLock::new(AnyNode::with_value(NodeValue::Bool(v)))),
ConfigValue::Int(v) => Arc::new(RwLock::new(AnyNode::with_value(NodeValue::Int(v)))),
ConfigValue::Float(v) => Arc::new(RwLock::new(AnyNode::with_value(NodeValue::Float(v)))),
ConfigValue::String(v) => Arc::new(RwLock::new(AnyNode::with_value(NodeValue::String(v)))),
ConfigValue::Bytes(v) => Arc::new(RwLock::new(AnyNode::with_value(NodeValue::Bytes(v)))),
ConfigValue::List(v) => {
let child = OmegaConf::create_list(v);
Arc::new(RwLock::new(child))
}
ConfigValue::Dict(v) => {
let child = OmegaConf::create_dict(v);
Arc::new(RwLock::new(child))
}
ConfigValue::Interpolation(v) => Arc::new(RwLock::new(AnyNode::interpolation(v))),
}
}