| @@ -2139,16 +2139,6 @@ dependencies = [ | |||
| "syn 1.0.109", | |||
| ] | |||
| [[package]] | |||
| name = "derive" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "proc-macro2", | |||
| "quote", | |||
| "quote_into", | |||
| "syn 1.0.109", | |||
| ] | |||
| [[package]] | |||
| name = "digest" | |||
| version = "0.10.7" | |||
| @@ -2456,7 +2446,6 @@ name = "dora-node-api-python" | |||
| version = "0.3.4" | |||
| dependencies = [ | |||
| "arrow", | |||
| "derive", | |||
| "dora-node-api", | |||
| "dora-operator-api-python", | |||
| "dora-ros2-bridge-python", | |||
| @@ -2465,6 +2454,7 @@ dependencies = [ | |||
| "flume 0.10.14", | |||
| "futures", | |||
| "pyo3", | |||
| "python_special_method_derive", | |||
| "pythonize", | |||
| "serde_yaml 0.8.26", | |||
| ] | |||
| @@ -6507,6 +6497,7 @@ dependencies = [ | |||
| "cfg-if 1.0.0", | |||
| "eyre", | |||
| "indoc", | |||
| "inventory", | |||
| "libc", | |||
| "memoffset 0.9.1", | |||
| "parking_lot", | |||
| @@ -6563,6 +6554,17 @@ dependencies = [ | |||
| "syn 2.0.65", | |||
| ] | |||
| [[package]] | |||
| name = "python_special_method_derive" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "proc-macro2", | |||
| "pyo3", | |||
| "quote", | |||
| "quote_into", | |||
| "syn 1.0.109", | |||
| ] | |||
| [[package]] | |||
| name = "pythonize" | |||
| version = "0.21.1" | |||
| @@ -34,7 +34,7 @@ members = [ | |||
| "libraries/extensions/ros2-bridge", | |||
| "libraries/extensions/ros2-bridge/msg-gen", | |||
| "libraries/extensions/ros2-bridge/python", | |||
| "derive", | |||
| "python_special_method_derive", | |||
| ] | |||
| [workspace.package] | |||
| @@ -67,12 +67,12 @@ dora-coordinator = { version = "0.3.4", path = "binaries/coordinator" } | |||
| dora-ros2-bridge = { path = "libraries/extensions/ros2-bridge" } | |||
| dora-ros2-bridge-msg-gen = { path = "libraries/extensions/ros2-bridge/msg-gen" } | |||
| dora-ros2-bridge-python = { path = "libraries/extensions/ros2-bridge/python" } | |||
| derive = { path = "derive" } | |||
| python_special_method_derive = { path = "python_special_method_derive" } | |||
| arrow = { version = "52" } | |||
| arrow-schema = { version = "52" } | |||
| arrow-data = { version = "52" } | |||
| arrow-array = { version = "52" } | |||
| pyo3 = "0.21" | |||
| pyo3 = { version = "0.21", workspace = true, features = ["eyre", "abi3-py37", "multiple-pymethods"] } | |||
| pythonize = "0.21" | |||
| [package] | |||
| @@ -16,7 +16,7 @@ telemetry = ["dora-runtime/telemetry"] | |||
| [dependencies] | |||
| dora-node-api = { workspace = true } | |||
| dora-operator-api-python = { workspace = true } | |||
| pyo3 = { workspace = true, features = ["eyre", "abi3-py37"] } | |||
| pyo3.workspace = true | |||
| eyre = "0.6" | |||
| serde_yaml = "0.8.23" | |||
| flume = "0.10.14" | |||
| @@ -25,7 +25,7 @@ arrow = { workspace = true, features = ["pyarrow"] } | |||
| pythonize = { workspace = true } | |||
| futures = "0.3.28" | |||
| dora-ros2-bridge-python = { workspace = true } | |||
| derive = { workspace = true } | |||
| python_special_method_derive = { workspace = true } | |||
| [lib] | |||
| name = "dora" | |||
| @@ -24,7 +24,7 @@ use pyo3::types::{PyBytes, PyDict}; | |||
| /// ``` | |||
| /// | |||
| #[pyclass] | |||
| #[derive(derive::DirHelper)] | |||
| #[derive(python_special_method_derive::DirHelper)] | |||
| pub struct Node { | |||
| events: Events, | |||
| node: DoraNode, | |||
| @@ -198,13 +198,6 @@ impl Node { | |||
| Ok(()) | |||
| } | |||
| // TODO: We should only list fields which are at least readableby Python users, right? | |||
| /// Get a list of the fields of this Node. | |||
| pub fn __dir__(&self) -> Vec<String> { | |||
| self.fields() | |||
| } | |||
| } | |||
| enum Events { | |||
| @@ -1,5 +1,5 @@ | |||
| [package] | |||
| name = "derive" | |||
| name = "python_special_method_derive" | |||
| version = "0.1.0" | |||
| edition = "2021" | |||
| @@ -8,6 +8,7 @@ syn = "1.0" | |||
| quote = "1.0" | |||
| proc-macro2 = "1.0" | |||
| quote_into = "0.2.0" | |||
| pyo3.workspace = true | |||
| [lib] | |||
| proc-macro = true | |||
| @@ -5,6 +5,8 @@ use proc_macro::TokenStream; | |||
| use quote::quote; | |||
| use syn::{parse_macro_input, Data, DeriveInput, Fields}; | |||
| // TODO: We should only list fields which are at least readableby Python users, right? | |||
| /// Add a `fields` method to the struct. | |||
| /// | |||
| /// Because we cannot have multiple `#[pymethods]` impls, this macro | |||
| @@ -37,8 +39,9 @@ pub fn dir_helper_derive(input: TokenStream) -> TokenStream { | |||
| } | |||
| }];); | |||
| quote! { | |||
| #[pyo3::pymethods] | |||
| impl #name { | |||
| pub fn fields(&self) -> Vec<String> { | |||
| pub fn __dir__(&self) -> Vec<String> { | |||
| let mut names = Vec::new(); | |||
| #assigner | |||
| names | |||
| @@ -49,8 +52,9 @@ pub fn dir_helper_derive(input: TokenStream) -> TokenStream { | |||
| Fields::Unit => { | |||
| // If the struct has no fields | |||
| quote! { | |||
| #[pyo3::pymethods] | |||
| impl #name { | |||
| pub fn fields(&self) -> Vec<String> { | |||
| pub fn __dir__(&self) -> Vec<String> { | |||
| Vec::new() | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| use derive::DirHelper; | |||
| use pyo3::pyclass; | |||
| use python_special_method_derive::DirHelper; | |||
| #[pyclass] | |||
| #[derive(DirHelper)] | |||
| #[allow(dead_code)] | |||
| struct WithFields { | |||
| @@ -17,7 +19,7 @@ fn test_with_fields() { | |||
| my: "".to_string(), | |||
| name: 0.0, | |||
| } | |||
| .fields(); | |||
| .__dir__(); | |||
| assert_eq!( | |||
| vec![ | |||
| "hello".to_string(), | |||
| @@ -29,12 +31,13 @@ fn test_with_fields() { | |||
| ); | |||
| } | |||
| #[pyclass] | |||
| #[derive(DirHelper)] | |||
| #[allow(dead_code)] | |||
| struct UnitNoFields; | |||
| #[test] | |||
| fn test_no_fields() { | |||
| let fields: Vec<String> = UnitNoFields.fields(); | |||
| let fields: Vec<String> = UnitNoFields.__dir__(); | |||
| assert_eq!(Vec::<String>::new(), fields); | |||
| } | |||