Skip to content

Commit

Permalink
Merge branch 'main' of github.com:NREL/altrios into v0.1.5
Browse files Browse the repository at this point in the history
  • Loading branch information
calbaker committed Jan 12, 2024
2 parents 1a8c186 + 9d2efbe commit 4285969
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 182 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/wheels.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# test, build, and release python wheels
name: wheels

# trigger whenever a release is published
on:
release:
types: [published]

# run tests, build, upload
jobs:
call-tests:
uses: nrel/altrios/.github/workflows/tests.yaml@main
Expand Down
118 changes: 85 additions & 33 deletions rust/altrios-core/altrios-proc-macros/src/altrios_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,23 @@ pub(crate) fn altrios_api(attr: TokenStream, item: TokenStream) -> TokenStream {
}
/// Rust-defined `__getitem__` magic method for Python used exposed via PyO3.
/// Prevents the Python user getting item directly using indexing.
fn __getitem__(&self, _idx: usize) -> PyResult<()> {
Err(PyNotImplementedError::new_err(
fn __getitem__(&self, _idx: usize) -> anyhow::Result<()> {
bail!(PyNotImplementedError::new_err(
"Getting Rust vector value at index is not implemented.
Run `tolist` method to convert to standalone Python list.",
))
}
/// Rust-defined `__setitem__` magic method for Python used exposed via PyO3.
/// Prevents the Python user setting item using indexing.
fn __setitem__(&mut self, _idx: usize, _new_value: #contained_dtype) -> PyResult<()> {
Err(PyNotImplementedError::new_err(
fn __setitem__(&mut self, _idx: usize, _new_value: #contained_dtype) -> anyhow::Result<()> {
bail!(PyNotImplementedError::new_err(
"Setting list value at index is not implemented.
Run `tolist` method, modify value at index, and
then set entire list.",
))
}
/// PyO3-exposed method to convert vec-containing struct to Python list.
fn tolist(&self) -> PyResult<Vec<#contained_dtype>> {
fn tolist(&self) -> anyhow::Result<Vec<#contained_dtype>> {
Ok(self.0.clone())
}
/// Rust-defined `__len__` magic method for Python used exposed via PyO3.
Expand Down Expand Up @@ -164,47 +164,86 @@ pub(crate) fn altrios_api(attr: TokenStream, item: TokenStream) -> TokenStream {
#[staticmethod]
#[pyo3(name = "default")]
/// Exposes `default` to python.
fn default_py() -> PyResult<Self> {
fn default_py() -> anyhow::Result<Self> {
Ok(Self::default())
}

/// json serialization method.
/// Write (serialize) an object into a string
///
/// # Arguments:
///
/// * `format`: `str` - The target format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
///
#[pyo3(name = "to_str")]
pub fn to_str_py(&self, format: &str) -> anyhow::Result<String> {
self.to_str(format)
}

/// Read (deserialize) an object from a string
///
/// # Arguments:
///
/// * `contents`: `str` - The string containing the object data
/// * `format`: `str` - The source format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
///
#[staticmethod]
#[pyo3(name = "from_str")]
pub fn from_str_py(contents: &str, format: &str) -> anyhow::Result<Self> {
Self::from_str(contents, format)
}

/// Write (serialize) an object to a JSON string
#[pyo3(name = "to_json")]
fn to_json_py(&self) -> PyResult<String> {
Ok(self.to_json())
fn to_json_py(&self) -> anyhow::Result<String> {
self.to_json()
}

/// Read (deserialize) an object to a JSON string
///
/// # Arguments
///
/// * `json_str`: `str` - JSON-formatted string to deserialize from
///
#[staticmethod]
/// json deserialization method.
#[pyo3(name = "from_json")]
fn from_json_py(json_str: &str) -> PyResult<Self> {
Ok(Self::from_json(json_str)?)
fn from_json_py(json_str: &str) -> anyhow::Result<Self> {
Self::from_json(json_str)
}

/// yaml serialization method.
/// Write (serialize) an object to a YAML string
#[pyo3(name = "to_yaml")]
fn to_yaml_py(&self) -> PyResult<String> {
Ok(self.to_yaml())
fn to_yaml_py(&self) -> anyhow::Result<String> {
self.to_yaml()
}

/// Read (deserialize) an object from a YAML string
///
/// # Arguments
///
/// * `yaml_str`: `str` - YAML-formatted string to deserialize from
///
#[staticmethod]
/// yaml deserialization method.
#[pyo3(name = "from_yaml")]
fn from_yaml_py(yaml_str: &str) -> PyResult<Self> {
Ok(Self::from_yaml(yaml_str)?)
fn from_yaml_py(yaml_str: &str) -> anyhow::Result<Self> {
Self::from_yaml(yaml_str)
}

/// bincode serialization method.
/// Write (serialize) an object to bincode-encoded `bytes`
#[pyo3(name = "to_bincode")]
fn to_bincode_py<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> {
Ok(PyBytes::new(py, &self.to_bincode()))
fn to_bincode_py<'py>(&self, py: Python<'py>) -> anyhow::Result<&'py PyBytes> {
Ok(PyBytes::new(py, &self.to_bincode()?))
}

/// Read (deserialize) an object from bincode-encoded `bytes`
///
/// # Arguments
///
/// * `encoded`: `bytes` - Encoded bytes to deserialize from
///
#[staticmethod]
/// bincode deserialization method.
#[pyo3(name = "from_bincode")]
fn from_bincode_py(encoded: &PyBytes) -> PyResult<Self> {
Ok(Self::from_bincode(encoded.as_bytes())?)
fn from_bincode_py(encoded: &PyBytes) -> anyhow::Result<Self> {
Self::from_bincode(encoded.as_bytes())
}

/// `__copy__` magic method that uses `clone`.
Expand All @@ -231,17 +270,30 @@ pub(crate) fn altrios_api(attr: TokenStream, item: TokenStream) -> TokenStream {
impl #ident {
#py_impl_block

#[staticmethod]
#[pyo3(name = "from_file")]
/// Exposes `from_file` to Python.
fn from_file_py(filename: String) -> PyResult<Self> {
Ok(Self::from_file(&filename)?)
/// Write (serialize) an object to a file.
/// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
/// Creates a new file if it does not already exist, otherwise truncates the existing file.
///
/// # Arguments
///
/// * `filepath`: `str | pathlib.Path` - The filepath at which to write the object
///
#[pyo3(name = "to_file")]
pub fn to_file_py(&self, filepath: &PyAny) -> anyhow::Result<()> {
self.to_file(PathBuf::extract(filepath)?)
}

#[pyo3(name = "to_file")]
/// Exposes `to_file` to Python.
fn to_file_py(&self, filename: &str) -> PyResult<()> {
Ok(self.to_file(filename)?)
/// Read (deserialize) an object from a file.
/// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
///
/// # Arguments:
///
/// * `filepath`: `str | pathlib.Path` - The filepath from which to read the object
///
#[staticmethod]
#[pyo3(name = "from_file")]
pub fn from_file_py(filepath: &PyAny) -> anyhow::Result<Self> {
Self::from_file(PathBuf::extract(filepath)?)
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn impl_get_set_si(

impl_block.extend::<TokenStream2>(quote! {
#[getter]
fn #get_name(&self) -> PyResult<#get_type> {
fn #get_name(&self) -> anyhow::Result<#get_type> {
Ok(#field_val)
}
});
Expand All @@ -82,7 +82,7 @@ fn impl_get_set_si(

impl_block.extend::<TokenStream2>(quote! {
#[setter(#setter_rename)]
fn #set_err(&mut self, new_val: f64) -> PyResult<()> {
fn #set_err(&mut self, new_val: f64) -> anyhow::Result<()> {
self.#field = #field_type::new::<#field_units>(new_val);
Ok(())
}
Expand All @@ -91,8 +91,8 @@ fn impl_get_set_si(
// Directly setting value raises error to prevent nested struct issues
impl_block.extend::<TokenStream2>(quote! {
#[setter]
fn #set_name(&mut self, new_val: f64) -> PyResult<()> {
Err(PyAttributeError::new_err(DIRECT_SET_ERR))
fn #set_name(&mut self, new_val: f64) -> anyhow::Result<()> {
bail!(PyAttributeError::new_err(DIRECT_SET_ERR))
}
});
}
Expand Down Expand Up @@ -126,7 +126,7 @@ fn impl_get_body(

impl_block.extend::<TokenStream2>(quote! {
#[getter]
fn #get_name(&self) -> PyResult<#field_type> {
fn #get_name(&self) -> anyhow::Result<#field_type> {
Ok(#field_val)
}
});
Expand All @@ -152,7 +152,7 @@ fn impl_set_body(

impl_block.extend::<TokenStream2>(quote! {
#[setter(#setter_rename)]
fn #set_err(&mut self, new_val: #field_type) -> PyResult<()> {
fn #set_err(&mut self, new_val: #field_type) -> anyhow::Result<()> {
self.#field = new_val;
Ok(())
}
Expand All @@ -161,8 +161,8 @@ fn impl_set_body(
// Directly setting value raises error to prevent nested struct issues
impl_block.extend::<TokenStream2>(quote! {
#[setter]
fn #set_name(&mut self, new_val: #field_type) -> PyResult<()> {
Err(PyAttributeError::new_err(DIRECT_SET_ERR))
fn #set_name(&mut self, new_val: #field_type) -> anyhow::Result<()> {
bail!(PyAttributeError::new_err(DIRECT_SET_ERR))
}
});
}
Expand Down
18 changes: 9 additions & 9 deletions rust/altrios-core/src/consist/consist_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,36 @@ use super::*;
fn __new__(
loco_vec: Vec<Locomotive>,
save_interval: Option<usize>
) -> PyResult<Self> {
) -> anyhow::Result<Self> {
Ok(Self::new(loco_vec, save_interval, PowerDistributionControlType::default()))
}
#[getter("loco_vec")]
fn get_loco_vec_py(&self) -> PyResult<Pyo3VecLocoWrapper> {
fn get_loco_vec_py(&self) -> anyhow::Result<Pyo3VecLocoWrapper> {
Ok(Pyo3VecLocoWrapper(self.loco_vec.clone()))
}
#[setter("loco_vec")]
fn set_loco_vec_py(&mut self, loco_vec: Vec<Locomotive>) -> PyResult<()> {
fn set_loco_vec_py(&mut self, loco_vec: Vec<Locomotive>) -> anyhow::Result<()> {
self.set_loco_vec(loco_vec);
Ok(())
}
#[pyo3(name="drain_loco_vec")]
fn drain_loco_vec_py(&mut self, start: usize, end: usize) -> PyResult<Pyo3VecLocoWrapper> {
fn drain_loco_vec_py(&mut self, start: usize, end: usize) -> anyhow::Result<Pyo3VecLocoWrapper> {
Ok(Pyo3VecLocoWrapper(self.drain_loco_vec(start, end)))
}
#[pyo3(name = "set_save_interval")]
/// Set save interval and cascade to nested components.
fn set_save_interval_py(&mut self, save_interval: Option<usize>) -> PyResult<()> {
fn set_save_interval_py(&mut self, save_interval: Option<usize>) -> anyhow::Result<()> {
self.set_save_interval(save_interval);
Ok(())
}
#[pyo3(name = "get_save_interval")]
/// Set save interval and cascade to nested components.
fn get_save_interval_py(&self) -> PyResult<Option<usize>> {
fn get_save_interval_py(&self) -> anyhow::Result<Option<usize>> {
Ok(self.get_save_interval())
}
Expand Down Expand Up @@ -82,17 +82,17 @@ use super::*;
}
#[getter("force_max_lbs")]
fn get_force_max_pounds_py(&self) -> PyResult<f64> {
fn get_force_max_pounds_py(&self) -> anyhow::Result<f64> {
Ok(self.force_max()?.get::<si::pound_force>())
}
#[getter("force_max_newtons")]
fn get_force_max_newtons_py(&self) -> PyResult<f64> {
fn get_force_max_newtons_py(&self) -> anyhow::Result<f64> {
Ok(self.force_max()?.get::<si::newton>())
}
#[getter("mass_kg")]
fn get_mass_kg_py(&self) -> PyResult<Option<f64>> {
fn get_mass_kg_py(&self) -> anyhow::Result<Option<f64>> {
Ok(self.mass()?.map(|m| m.get::<si::kilogram>()))
}
)]
Expand Down
4 changes: 2 additions & 2 deletions rust/altrios-core/src/consist/consist_sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ use crate::imports::*;
}
#[pyo3(name = "get_save_interval")]
fn get_save_interval_py(&self) -> PyResult<Option<usize>> {
fn get_save_interval_py(&self) -> anyhow::Result<Option<usize>> {
Ok(self.loco_con.get_save_interval())
}
#[pyo3(name = "trim_failed_steps")]
fn trim_failed_steps_py(&mut self) -> PyResult<()> {
fn trim_failed_steps_py(&mut self) -> anyhow::Result<()> {
self.trim_failed_steps()?;
Ok(())
}
Expand Down
8 changes: 4 additions & 4 deletions rust/altrios-core/src/consist/locomotive/loco_sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::imports::*;
time_seconds: Vec<f64>,
pwr_watts: Vec<f64>,
engine_on: Vec<Option<bool>>,
) -> PyResult<Self> {
) -> anyhow::Result<Self> {
Ok(Self::new(time_seconds, pwr_watts, engine_on))
}
Expand Down Expand Up @@ -157,18 +157,18 @@ pub struct PowerTraceElement {
#[pyo3(name = "set_save_interval")]
/// Set save interval and cascade to nested components.
fn set_save_interval_py(&mut self, save_interval: Option<usize>) -> PyResult<()> {
fn set_save_interval_py(&mut self, save_interval: Option<usize>) -> anyhow::Result<()> {
self.set_save_interval(save_interval);
Ok(())
}
#[pyo3(name = "get_save_interval")]
fn get_save_interval_py(&self) -> PyResult<Option<usize>> {
fn get_save_interval_py(&self) -> anyhow::Result<Option<usize>> {
Ok(self.loco_unit.get_save_interval())
}
#[pyo3(name = "trim_failed_steps")]
fn trim_failed_steps_py(&mut self) -> PyResult<()> {
fn trim_failed_steps_py(&mut self) -> anyhow::Result<()> {
self.trim_failed_steps()?;
Ok(())
}
Expand Down
Loading

0 comments on commit 4285969

Please sign in to comment.