toolapi 0.4.2__tar.gz → 0.4.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -961,9 +961,9 @@ dependencies = [
961
961
 
962
962
  [[package]]
963
963
  name = "toolapi"
964
- version = "0.4.2"
964
+ version = "0.4.5"
965
965
  source = "registry+https://github.com/rust-lang/crates.io-index"
966
- checksum = "91435d6c60b8b2962687d5e379e1c240ac2d17e49f03397da6272666843db522"
966
+ checksum = "043b833acff2be4f99063a7fa50676c96fddb7a835a88c21e76e21a0adbdba64"
967
967
  dependencies = [
968
968
  "axum",
969
969
  "futures",
@@ -984,7 +984,7 @@ dependencies = [
984
984
 
985
985
  [[package]]
986
986
  name = "toolapi-py"
987
- version = "0.4.2"
987
+ version = "0.4.5"
988
988
  dependencies = [
989
989
  "num-complex",
990
990
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "toolapi-py"
3
- version = "0.4.2"
3
+ version = "0.4.5"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -12,4 +12,4 @@ crate-type = ["cdylib"]
12
12
  [dependencies]
13
13
  num-complex = "0.4.6"
14
14
  pyo3 = { version = "0.27.1", features = ["extension-module", "abi3-py39", "num-complex"] }
15
- toolapi = { version = "0.4.2", features = ["client", "pyo3"] }
15
+ toolapi = { version = "0.4.5", features = ["client", "pyo3"] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toolapi
3
- Version: 0.4.2
3
+ Version: 0.4.5
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -0,0 +1,44 @@
1
+ use pyo3::prelude::*;
2
+
3
+ /// A Python module implemented in Rust.
4
+ #[pymodule]
5
+ mod _core {
6
+ use super::*;
7
+ use pyo3::exceptions::PyException;
8
+
9
+ #[pyfunction]
10
+ #[pyo3(signature = (address, input, on_message=None))]
11
+ fn call(
12
+ py: Python<'_>,
13
+ address: &str,
14
+ input: toolapi::Value,
15
+ on_message: Option<Py<PyAny>>,
16
+ ) -> PyResult<toolapi::Value> {
17
+ // Wraps the user callback, returns `true` (continue tool) if:
18
+ // - no callback was provided
19
+ // - callback returned true
20
+ // Returns `false` (abort the tool) if:
21
+ // - callback raised an exception
22
+ // - return value was not a bool
23
+ // - callback returned false
24
+ let on_message = |msg: String| -> bool {
25
+ match on_message.as_ref() {
26
+ // User provided a callback: try to call it
27
+ Some(func) => Python::attach(|py| {
28
+ match func.call1(py, (msg,)) {
29
+ // Call succeeded: convert result to bool (false on error)
30
+ Ok(ret) => ret.extract(py).unwrap_or(false),
31
+ // Callback raised an exception: stop tool
32
+ Err(_) => false,
33
+ }
34
+ }),
35
+ // No user callback: don't stop tool
36
+ None => true,
37
+ }
38
+ };
39
+
40
+ // Run the tool and return the result - pyo3 will convert Value to python
41
+ py.detach(|| toolapi::call(address, input, on_message))
42
+ .map_err(|err| PyException::new_err(format!("ToolCallError: {err}")))
43
+ }
44
+ }
@@ -98,13 +98,13 @@ class SegmentedPhantom:
98
98
  Rust::
99
99
 
100
100
  pub struct SegmentedPhantom {
101
- pub tissues: Vec<PhantomTissue>,
101
+ pub tissues: HashMap<String, PhantomTissue>,
102
102
  pub b1_tx: Vec<Volume>,
103
103
  pub b1_rx: Vec<Volume>,
104
104
  }
105
105
  """
106
106
 
107
- tissues: list[PhantomTissue]
107
+ tissues: dict[str, PhantomTissue]
108
108
  b1_tx: list[Volume] = field(default_factory=list)
109
109
  b1_rx: list[Volume] = field(default_factory=list)
110
110
 
toolapi-0.4.2/src/lib.rs DELETED
@@ -1,304 +0,0 @@
1
- use pyo3::types::{PyDict, PyList};
2
- use pyo3::{IntoPyObjectExt, prelude::*};
3
- use toolapi::value::structured::{InstantSeqEvent, PhantomTissue, Volume};
4
- use toolapi::value::typed::{TypedDict, TypedList};
5
-
6
- // TODO: we should register new PyException classes for errors and use those in the code below
7
-
8
- /// A Python module implemented in Rust.
9
- #[pymodule]
10
- mod _core {
11
- use pyo3::exceptions::PyException;
12
-
13
- use super::*;
14
-
15
- #[pyfunction]
16
- #[pyo3(signature = (address, input, on_message=None))]
17
- fn call(
18
- py: Python<'_>,
19
- address: &str,
20
- input: toolapi::Value,
21
- on_message: Option<Py<PyAny>>,
22
- ) -> PyResult<Py<PyAny>> {
23
- // Wraps the user callback, returns `true` (continue tool) if:
24
- // - no callback was provided
25
- // - callback returned true
26
- // Returns `false` (abort the tool) if:
27
- // - callback raised an exception
28
- // - return value was not a bool
29
- // - callback returned false
30
- let on_message = |msg: String| -> bool {
31
- match on_message.as_ref() {
32
- // User provided a callback: try to call it
33
- Some(func) => Python::attach(|py| {
34
- match func.call1(py, (msg,)) {
35
- // Call succeeded: convert result to bool (false on error)
36
- Ok(ret) => ret.extract(py).unwrap_or(false),
37
- // Callback raised an exception: stop tool
38
- Err(_) => false,
39
- }
40
- }),
41
- // No user callback: don't stop tool
42
- None => true,
43
- }
44
- };
45
-
46
- let result = py
47
- .detach(|| toolapi::call(address, input, on_message))
48
- .map_err(|err| PyException::new_err(format!("ToolCallError: {err}")));
49
-
50
- result.and_then(|value| value_to_obj(py, value))
51
- }
52
- }
53
-
54
- // =============================================================================
55
- // Rust Value -> Python conversion
56
- // =============================================================================
57
-
58
- // TODO: could implement IntoPyObject in `toolapi` under `pyo3` feature flag
59
-
60
- fn value_to_obj(py: Python<'_>, value: toolapi::Value) -> PyResult<Py<PyAny>> {
61
- match value {
62
- toolapi::Value::None(()) => Ok(py.None()),
63
- toolapi::Value::Bool(b) => b.into_py_any(py),
64
- toolapi::Value::Int(i) => i.into_py_any(py),
65
- toolapi::Value::Float(f) => f.into_py_any(py),
66
- toolapi::Value::Str(s) => s.into_py_any(py),
67
- toolapi::Value::Complex(c) => c.into_py_any(py),
68
-
69
- toolapi::Value::Vec3(v) => {
70
- let module = py.import("toolapi.value")?;
71
- let cls = module.getattr("Vec3")?;
72
- cls.call1((v.0.to_vec(),)).map(|o| o.unbind())
73
- }
74
- toolapi::Value::Vec4(v) => {
75
- let module = py.import("toolapi.value")?;
76
- let cls = module.getattr("Vec4")?;
77
- cls.call1((v.0.to_vec(),)).map(|o| o.unbind())
78
- }
79
-
80
- toolapi::Value::InstantSeqEvent(event) => instant_seq_event_to_obj(py, event),
81
-
82
- toolapi::Value::Volume(vol) => volume_to_obj(py, vol),
83
-
84
- toolapi::Value::PhantomTissue(pt) => phantom_tissue_to_obj(py, pt),
85
-
86
- toolapi::Value::SegmentedPhantom(sp) => {
87
- let module = py.import("toolapi.value")?;
88
- let cls = module.getattr("SegmentedPhantom")?;
89
- let tissues = PyList::empty(py);
90
- for t in sp.tissues {
91
- let obj = phantom_tissue_to_obj(py, t)?;
92
- tissues.append(obj)?;
93
- }
94
- let b1_tx = PyList::empty(py);
95
- for v in sp.b1_tx {
96
- let obj = volume_to_obj(py, v)?;
97
- b1_tx.append(obj)?;
98
- }
99
- let b1_rx = PyList::empty(py);
100
- for v in sp.b1_rx {
101
- let obj = volume_to_obj(py, v)?;
102
- b1_rx.append(obj)?;
103
- }
104
- cls.call1((tissues, b1_tx, b1_rx)).map(|o| o.unbind())
105
- }
106
-
107
- toolapi::Value::Dict(d) => {
108
- let dict = PyDict::new(py);
109
- for (key, value) in d.0.into_iter() {
110
- let obj = value_to_obj(py, value)?;
111
- dict.set_item(key, obj)?;
112
- }
113
- dict.into_py_any(py)
114
- }
115
- toolapi::Value::List(l) => {
116
- let list = PyList::empty(py);
117
- for item in l.0 {
118
- let obj = value_to_obj(py, item)?;
119
- list.append(obj)?;
120
- }
121
- list.into_py_any(py)
122
- }
123
-
124
- toolapi::Value::TypedList(tl) => typed_list_to_obj(py, tl),
125
- toolapi::Value::TypedDict(td) => typed_dict_to_obj(py, td),
126
- }
127
- }
128
-
129
- fn instant_seq_event_to_obj(py: Python<'_>, event: InstantSeqEvent) -> PyResult<Py<PyAny>> {
130
- let module = py.import("toolapi.value")?;
131
- let cls = module.getattr("InstantSeqEvent")?;
132
- match event {
133
- InstantSeqEvent::Pulse { angle, phase } => cls
134
- .call_method1("Pulse", (angle, phase))
135
- .map(|o| o.unbind()),
136
- InstantSeqEvent::Fid { kt } => {
137
- // Build a Vec4 wrapper for the kt field
138
- let vec4_cls = module.getattr("Vec4")?;
139
- let kt_obj = vec4_cls.call1((kt.0.to_vec(),))?;
140
- cls.call_method1("Fid", (kt_obj,)).map(|o| o.unbind())
141
- }
142
- InstantSeqEvent::Adc { phase } => cls.call_method1("Adc", (phase,)).map(|o| o.unbind()),
143
- }
144
- }
145
-
146
- fn volume_to_obj(py: Python<'_>, vol: Volume) -> PyResult<Py<PyAny>> {
147
- let module = py.import("toolapi.value")?;
148
- let cls = module.getattr("Volume")?;
149
- let shape = vol.shape.to_vec();
150
- let affine: Vec<Vec<f64>> = vol.affine.iter().map(|row| row.to_vec()).collect();
151
- let data = typed_list_to_py_list(py, vol.data)?;
152
- cls.call1((shape, affine, data)).map(|o| o.unbind())
153
- }
154
-
155
- fn phantom_tissue_to_obj(py: Python<'_>, pt: PhantomTissue) -> PyResult<Py<PyAny>> {
156
- let module = py.import("toolapi.value")?;
157
- let cls = module.getattr("PhantomTissue")?;
158
- let density = volume_to_obj(py, pt.density)?;
159
- let db0 = volume_to_obj(py, pt.db0)?;
160
- cls.call1((density, db0, pt.t1, pt.t2, pt.t2dash, pt.adc))
161
- .map(|o| o.unbind())
162
- }
163
-
164
- /// Convert a TypedList into a plain Python list.
165
- fn typed_list_to_py_list(py: Python<'_>, tl: TypedList) -> PyResult<Py<PyList>> {
166
- let list = match tl {
167
- TypedList::None(v) => {
168
- let l = PyList::empty(py);
169
- for _ in v {
170
- l.append(py.None())?;
171
- }
172
- l
173
- }
174
- TypedList::Bool(v) => PyList::new(py, v)?,
175
- TypedList::Int(v) => PyList::new(py, v)?,
176
- TypedList::Float(v) => PyList::new(py, v)?,
177
- TypedList::Str(v) => PyList::new(py, v)?,
178
- TypedList::Complex(v) => PyList::new(py, v)?,
179
- TypedList::Vec3(v) => {
180
- let l = PyList::empty(py);
181
- let module = py.import("toolapi.value")?;
182
- let cls = module.getattr("Vec3")?;
183
- for item in v {
184
- l.append(cls.call1((item.0.to_vec(),))?)?;
185
- }
186
- l
187
- }
188
- TypedList::Vec4(v) => {
189
- let l = PyList::empty(py);
190
- let module = py.import("toolapi.value")?;
191
- let cls = module.getattr("Vec4")?;
192
- for item in v {
193
- l.append(cls.call1((item.0.to_vec(),))?)?;
194
- }
195
- l
196
- }
197
- TypedList::InstantSeqEvent(v) => {
198
- let l = PyList::empty(py);
199
- for item in v {
200
- l.append(instant_seq_event_to_obj(py, item)?)?;
201
- }
202
- l
203
- }
204
- TypedList::Volume(v) => {
205
- let l = PyList::empty(py);
206
- for item in v {
207
- l.append(volume_to_obj(py, item)?)?;
208
- }
209
- l
210
- }
211
- TypedList::SegmentedPhantom(v) => {
212
- let l = PyList::empty(py);
213
- for item in v {
214
- l.append(value_to_obj(py, toolapi::Value::SegmentedPhantom(item))?)?;
215
- }
216
- l
217
- }
218
- TypedList::PhantomTissue(v) => {
219
- let l = PyList::empty(py);
220
- for item in v {
221
- l.append(phantom_tissue_to_obj(py, item)?)?;
222
- }
223
- l
224
- }
225
- };
226
- Ok(list.unbind())
227
- }
228
-
229
- /// Convert a TypedList to a top-level Python object (for Value::TypedList).
230
- fn typed_list_to_obj(py: Python<'_>, tl: TypedList) -> PyResult<Py<PyAny>> {
231
- typed_list_to_py_list(py, tl).and_then(|l| l.into_py_any(py))
232
- }
233
-
234
- /// Convert a TypedDict to a Python dict.
235
- fn typed_dict_to_obj(py: Python<'_>, td: TypedDict) -> PyResult<Py<PyAny>> {
236
- let dict = PyDict::new(py);
237
- match td {
238
- TypedDict::None(m) => {
239
- for (k, _) in m {
240
- dict.set_item(k, py.None())?;
241
- }
242
- }
243
- TypedDict::Bool(m) => {
244
- for (k, v) in m {
245
- dict.set_item(k, v)?;
246
- }
247
- }
248
- TypedDict::Int(m) => {
249
- for (k, v) in m {
250
- dict.set_item(k, v)?;
251
- }
252
- }
253
- TypedDict::Float(m) => {
254
- for (k, v) in m {
255
- dict.set_item(k, v)?;
256
- }
257
- }
258
- TypedDict::Str(m) => {
259
- for (k, v) in m {
260
- dict.set_item(k, v)?;
261
- }
262
- }
263
- TypedDict::Complex(m) => {
264
- for (k, v) in m {
265
- dict.set_item(k, v)?;
266
- }
267
- }
268
- TypedDict::Vec3(m) => {
269
- let module = py.import("toolapi.value")?;
270
- let cls = module.getattr("Vec3")?;
271
- for (k, v) in m {
272
- dict.set_item(k, cls.call1((v.0.to_vec(),))?)?;
273
- }
274
- }
275
- TypedDict::Vec4(m) => {
276
- let module = py.import("toolapi.value")?;
277
- let cls = module.getattr("Vec4")?;
278
- for (k, v) in m {
279
- dict.set_item(k, cls.call1((v.0.to_vec(),))?)?;
280
- }
281
- }
282
- TypedDict::InstantSeqEvent(m) => {
283
- for (k, v) in m {
284
- dict.set_item(k, instant_seq_event_to_obj(py, v)?)?;
285
- }
286
- }
287
- TypedDict::Volume(m) => {
288
- for (k, v) in m {
289
- dict.set_item(k, volume_to_obj(py, v)?)?;
290
- }
291
- }
292
- TypedDict::SegmentedPhantom(m) => {
293
- for (k, v) in m {
294
- dict.set_item(k, value_to_obj(py, toolapi::Value::SegmentedPhantom(v))?)?;
295
- }
296
- }
297
- TypedDict::PhantomTissue(m) => {
298
- for (k, v) in m {
299
- dict.set_item(k, phantom_tissue_to_obj(py, v)?)?;
300
- }
301
- }
302
- }
303
- dict.into_py_any(py)
304
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes