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.
- {toolapi-0.4.2 → toolapi-0.4.5}/Cargo.lock +3 -3
- {toolapi-0.4.2 → toolapi-0.4.5}/Cargo.toml +2 -2
- {toolapi-0.4.2 → toolapi-0.4.5}/PKG-INFO +1 -1
- toolapi-0.4.5/src/lib.rs +44 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/src/toolapi/value.py +2 -2
- toolapi-0.4.2/src/lib.rs +0 -304
- {toolapi-0.4.2 → toolapi-0.4.5}/.github/workflows/pypi_publish.yml +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/.gitignore +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/CLAUDE.md +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/README.md +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/pyproject.toml +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/src/toolapi/__init__.py +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/src/toolapi/_core.pyi +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/demo_notebook.ipynb +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/demo_notebook_minimal.ipynb +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/load_phantom.py +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/pyproject.toml +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/test_toolapi.py +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/test/util.py +0 -0
- {toolapi-0.4.2 → toolapi-0.4.5}/uv.lock +0 -0
|
@@ -961,9 +961,9 @@ dependencies = [
|
|
|
961
961
|
|
|
962
962
|
[[package]]
|
|
963
963
|
name = "toolapi"
|
|
964
|
-
version = "0.4.
|
|
964
|
+
version = "0.4.5"
|
|
965
965
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
966
|
-
checksum = "
|
|
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.
|
|
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.
|
|
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.
|
|
15
|
+
toolapi = { version = "0.4.5", features = ["client", "pyo3"] }
|
toolapi-0.4.5/src/lib.rs
ADDED
|
@@ -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:
|
|
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:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|