Skip to content

Commit

Permalink
Merge pull request #38 from NREL/f2-doc-fields
Browse files Browse the repository at this point in the history
F2 doc fields
  • Loading branch information
calbaker authored Aug 15, 2023
2 parents c8e4919 + b892eac commit a210724
Show file tree
Hide file tree
Showing 20 changed files with 621 additions and 396 deletions.
3 changes: 2 additions & 1 deletion build_and_test.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(cd rust/ && cargo test) && \
(cd rust/fastsim-core/ && cargo test) && \
(cd rust/fastsim-cli/ && cargo test) && \
pip install -qe ".[dev]" && \
pytest -v python/fastsim/tests/
205 changes: 116 additions & 89 deletions python/fastsim/demos/demo.py

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions rust/fastsim-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fastsim-core"
version = "0.1.3"
version = "0.1.4"
edition = "2021"
license = "Apache-2.0"
authors = ["NREL/MTES/CIMS/MBAP Group <[email protected]>"]
Expand All @@ -10,7 +10,10 @@ readme = "../../README.md"
repository = "https://github.com/NREL/fastsim"

[dependencies]
proc-macros = { package = "fastsim-proc-macros", version = "~0" }
# uncomment next line for any development work in fastsim-proc-macros
proc-macros = { package = "fastsim-proc-macros", path = "./fastsim-proc-macros" }
# comment next line for any development work in fastsim-proc-macros
# proc-macros = { package = "fastsim-proc-macros", version = "0.1.4" }
pyo3 = { workspace = true, features = [
"extension-module",
"anyhow",
Expand Down
2 changes: 1 addition & 1 deletion rust/fastsim-core/fastsim-proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
authors = ["NREL/MTES/CIMS/MBAP Group <[email protected]>"]
name = "fastsim-proc-macros"
version = "0.1.3"
version = "0.1.4"
edition = "2021"
license = "Apache-2.0"
readme = "../../../README.md"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Module that implements [super::add_pyo3_api]

#[macro_use]
mod pyo3_api_utils;

use crate::imports::*;
use crate::utilities::*;

pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = syn::parse_macro_input!(item as syn::ItemStruct);
Expand Down Expand Up @@ -37,6 +41,7 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
// https://github.com/PyO3/pyo3/blob/48690525e19b87818b59f99be83f1e0eb203c7d4/pyo3-macros-backend/src/pyclass.rs#L220

let mut opts = FieldOptions::default();
// attributes to retain, i.e. attributes that are not handled by this macro
let keep: Vec<bool> = field
.attrs
.iter()
Expand Down Expand Up @@ -73,9 +78,14 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
})
.collect();
// println!("options {:?}", opts);
let mut iter = keep.iter();
// this drops attrs with api, removing the field attribute from the struct def
field.attrs.retain(|_| *iter.next().unwrap());
// this drops attrs matching `#[pyo3_api(...)]`, removing the field attribute from the struct def
let new_attrs: (Vec<&syn::Attribute>, Vec<bool>) = field
.attrs
.iter()
.zip(keep.iter())
.filter(|(_a, k)| **k)
.unzip();
field.attrs = new_attrs.0.iter().cloned().cloned().collect();

if let syn::Type::Path(type_path) = ftype.clone() {
// println!(
Expand Down Expand Up @@ -284,3 +294,60 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
final_output.extend::<TokenStream2>(output);
final_output.into()
}

#[derive(Debug, Default, Clone)]
pub struct FieldOptions {
/// if true, getters are not generated for a field
pub skip_get: bool,
/// if true, setters are not generated for a field
pub skip_set: bool,
/// if true, current field is itself a struct with `orphaned` field
pub field_has_orphaned: bool,
}

pub fn impl_getters_and_setters(
type_path: syn::TypePath,
impl_block: &mut TokenStream2,
ident: &proc_macro2::Ident,
opts: FieldOptions,
has_orphaned: bool,
ftype: syn::Type,
) {
let type_str = type_path.into_token_stream().to_string();
match type_str.as_str() {
"Array1 < f64 >" => {
impl_vec_get_set!(opts, ident, impl_block, f64, Pyo3ArrayF64, has_orphaned);
}
"Array1 < u32 >" => {
impl_vec_get_set!(opts, ident, impl_block, u32, Pyo3ArrayU32, has_orphaned);
}
"Array1 < i32 >" => {
impl_vec_get_set!(opts, ident, impl_block, i32, Pyo3ArrayI32, has_orphaned);
}
"Array1 < bool >" => {
impl_vec_get_set!(opts, ident, impl_block, bool, Pyo3ArrayBool, has_orphaned);
}
"Vec < f64 >" => {
impl_vec_get_set!(opts, ident, impl_block, f64, Pyo3VecF64, has_orphaned);
}
_ => match ident.to_string().as_str() {
"orphaned" => {
impl_block.extend::<TokenStream2>(quote! {
#[getter]
pub fn get_orphaned(&self) -> PyResult<bool> {
Ok(self.orphaned)
}
/// Reset the orphaned flag to false.
pub fn reset_orphaned(&mut self) -> PyResult<()> {
self.orphaned = false;
Ok(())
}
})
}
_ => {
impl_get_body!(ftype, ident, impl_block, opts);
impl_set_body!(ftype, ident, impl_block, has_orphaned, opts);
}
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
macro_rules! impl_vec_get_set {
($opts: ident, $fident: ident, $impl_block: ident, $contained_type: ty, $wrapper_type: expr, $has_orphaned: expr) => {
if !$opts.skip_get {
let get_name: TokenStream2 = format!("get_{}", $fident).parse().unwrap();
$impl_block.extend::<TokenStream2>(quote! {
#[getter]
pub fn #get_name(&self) -> PyResult<$wrapper_type> {
Ok($wrapper_type::new(self.#$fident.clone()))
}
});
}
if !$opts.skip_set {
let set_name: TokenStream2 = format!("set_{}", $fident).parse().unwrap();
match stringify!($wrapper_type) {
"Pyo3VecF64" => {
if $has_orphaned {
$impl_block.extend(quote! {
#[setter]
pub fn #set_name(&mut self, new_value: Vec<$contained_type>) -> PyResult<()> {
if !self.orphaned {
self.#$fident = new_value;
Ok(())
} else {
Err(PyAttributeError::new_err(crate::utils::NESTED_STRUCT_ERR))
}
}
})
} else {
$impl_block.extend(quote! {
#[setter]
pub fn #set_name(&mut self, new_value: Vec<$contained_type>) -> PyResult<()> {
self.#$fident = new_value;
Ok(())
}
})
}
}
_ => {
if $has_orphaned {
$impl_block.extend(quote! {
#[setter]
pub fn #set_name(&mut self, new_value: Vec<$contained_type>) -> PyResult<()> {
if !self.orphaned {
self.#$fident = Array1::from_vec(new_value);
Ok(())
} else {
Err(PyAttributeError::new_err(crate::utils::NESTED_STRUCT_ERR))
}
}
})
} else {
$impl_block.extend(quote! {
#[setter]
pub fn #set_name(&mut self, new_value: Vec<$contained_type>) -> PyResult<()> {
self.#$fident = Array1::from_vec(new_value);
Ok(())
}
})
}
}
}

}
};
}

/// Generates pyo3 getter methods
///
/// general match arguments:
/// - type: type of variable (e.g. `f64`)
/// - field: struct field
/// - impl_block: TokenStream2
/// - opts: FieldOptions struct instance
macro_rules! impl_get_body {
(
$type: ident, $field: ident, $impl_block: ident, $opts: ident
) => {
if !$opts.skip_get {
let get_name: TokenStream2 = format!("get_{}", $field).parse().unwrap();
let get_block = if $opts.field_has_orphaned {
quote! {
#[getter]
pub fn #get_name(&mut self) -> PyResult<#$type> {
self.#$field.orphaned = true;
Ok(self.#$field.clone())
}
}
} else {
quote! {
#[getter]
pub fn #get_name(&self) -> PyResult<#$type> {
Ok(self.#$field.clone())
}
}
};
$impl_block.extend::<TokenStream2>(get_block)
}
};
}

/// Generates pyo3 setter methods
///
/// general match arguments:
/// - type: type of variable (e.g. `f64`)
/// - field: struct field
/// - impl_block: TokenStream2
/// - has_orphaned: bool, true if struct has `orphaned` field
/// - opts: FieldOptions struct instance

macro_rules! impl_set_body {
( // for generic
$type: ident, $field: ident, $impl_block: ident, $has_orphaned: expr, $opts: ident
) => {
if !$opts.skip_set {
let set_name: TokenStream2 = format!("set_{}", $field).parse().unwrap();
let orphaned_set_block = if $has_orphaned && $opts.field_has_orphaned {
quote! {
if !self.orphaned {
self.#$field = new_value;
self.#$field.orphaned = false;
Ok(())
} else {
Err(PyAttributeError::new_err(crate::utils::NESTED_STRUCT_ERR))
}
}
} else if $has_orphaned {
quote! {
if !self.orphaned {
self.#$field = new_value;
Ok(())
} else {
Err(PyAttributeError::new_err(crate::utils::NESTED_STRUCT_ERR))
}
}
} else {
quote! {
self.#$field = new_value;
Ok(())
}
};

$impl_block.extend::<TokenStream2>(quote! {
#[setter]
pub fn #set_name(&mut self, new_value: #$type) -> PyResult<()> {
#orphaned_set_block
}
});
}
};
}

#[derive(Debug, Default, Clone)]
pub struct FieldOptions {
/// if true, getters are not generated for a field
pub skip_get: bool,
/// if true, setters are not generated for a field
pub skip_set: bool,
/// if true, current field is itself a struct with `orphaned` field
pub field_has_orphaned: bool,
}
2 changes: 2 additions & 0 deletions rust/fastsim-core/fastsim-proc-macros/src/approx_eq_derive.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Module that implements [super::approx_eq_derive]

use crate::imports::*;

pub fn approx_eq_derive(input: TokenStream) -> TokenStream {
Expand Down
Loading

0 comments on commit a210724

Please sign in to comment.