Skip to content

Commit 363aa0d

Browse files
authored
fix: fix tuning target formatting in python (#239)
1 parent 16c3d12 commit 363aa0d

4 files changed

Lines changed: 236 additions & 10 deletions

File tree

asic-rs-core/src/data/miner.rs

Lines changed: 215 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,10 @@ use crate::data::{
1919
serialize::{serialize_macaddr, serialize_power, serialize_temperature},
2020
};
2121

22-
#[cfg_attr(feature = "python", derive(asic_rs_pydantic::PyPydanticTaggedUnion))]
23-
#[cfg_attr(
24-
feature = "python",
25-
pydantic(discriminator = "type", value = "value", ref = "asic_rs.TuningTarget")
26-
)]
2722
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2823
pub enum TuningTarget {
29-
#[cfg_attr(feature = "python", pydantic(tag = "power"))]
3024
Power(Power),
31-
#[cfg_attr(feature = "python", pydantic(tag = "hashrate"))]
3225
HashRate(HashRate),
33-
#[cfg_attr(feature = "python", pydantic(tag = "mode"))]
3426
MiningMode(MiningMode),
3527
}
3628

@@ -124,3 +116,218 @@ pub struct MinerData {
124116
/// The current pools configured on the miner
125117
pub pools: Vec<PoolGroupData>,
126118
}
119+
120+
#[cfg(feature = "python")]
121+
pub use python_tuning_target::{TuningTargetHashRate, TuningTargetMode, TuningTargetPower};
122+
123+
#[cfg(feature = "python")]
124+
mod python_tuning_target {
125+
use asic_rs_pydantic::{
126+
PyPydanticType, PydanticSchemaMode, get_required_field, literal_schema,
127+
pydantic_typed_dict_schema, tagged_union_schema, union_schema,
128+
};
129+
use measurements::Power;
130+
use pyo3::{exceptions::PyValueError, prelude::*, types::PyAnyMethods};
131+
132+
use super::{HashRate, MiningMode, TuningTarget};
133+
134+
#[pyclass(from_py_object, module = "asic_rs")]
135+
#[derive(Debug, Clone)]
136+
pub struct TuningTargetPower {
137+
pub watts: f64,
138+
}
139+
140+
#[pymethods]
141+
impl TuningTargetPower {
142+
#[getter]
143+
fn watts(&self) -> f64 {
144+
self.watts
145+
}
146+
}
147+
148+
#[pyclass(from_py_object, module = "asic_rs")]
149+
#[derive(Debug, Clone)]
150+
pub struct TuningTargetHashRate {
151+
pub hashrate: HashRate,
152+
}
153+
154+
#[pymethods]
155+
impl TuningTargetHashRate {
156+
#[getter]
157+
fn hashrate(&self) -> HashRate {
158+
self.hashrate.clone()
159+
}
160+
}
161+
162+
#[pyclass(from_py_object, module = "asic_rs")]
163+
#[derive(Debug, Clone)]
164+
pub struct TuningTargetMode {
165+
pub mode: MiningMode,
166+
}
167+
168+
#[pymethods]
169+
impl TuningTargetMode {
170+
#[getter]
171+
fn mode(&self) -> MiningMode {
172+
self.mode
173+
}
174+
}
175+
176+
impl<'py> pyo3::IntoPyObject<'py> for TuningTarget {
177+
type Target = pyo3::PyAny;
178+
type Output = pyo3::Bound<'py, pyo3::PyAny>;
179+
type Error = pyo3::PyErr;
180+
181+
const OUTPUT_TYPE: pyo3::inspect::PyStaticExpr = {
182+
use pyo3::type_hint_union;
183+
type_hint_union!(
184+
<TuningTargetPower as pyo3::PyTypeInfo>::TYPE_HINT,
185+
<TuningTargetHashRate as pyo3::PyTypeInfo>::TYPE_HINT,
186+
<TuningTargetMode as pyo3::PyTypeInfo>::TYPE_HINT
187+
)
188+
};
189+
190+
fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
191+
match self {
192+
TuningTarget::Power(p) => TuningTargetPower {
193+
watts: p.as_watts(),
194+
}
195+
.into_pyobject(py)
196+
.map(pyo3::Bound::into_any),
197+
TuningTarget::HashRate(hr) => TuningTargetHashRate { hashrate: hr }
198+
.into_pyobject(py)
199+
.map(pyo3::Bound::into_any),
200+
TuningTarget::MiningMode(m) => TuningTargetMode { mode: m }
201+
.into_pyobject(py)
202+
.map(pyo3::Bound::into_any),
203+
}
204+
}
205+
}
206+
207+
impl PyPydanticType for TuningTarget {
208+
fn pydantic_schema<'py>(
209+
core_schema: &Bound<'py, PyAny>,
210+
mode: PydanticSchemaMode,
211+
) -> PyResult<Bound<'py, PyAny>> {
212+
let power_schema = pydantic_typed_dict_schema!(core_schema, "asic_rs.TuningTargetPower", {
213+
"type" => required(literal_schema(core_schema, &["power"])?),
214+
"value" => required(<Power as PyPydanticType>::pydantic_schema(core_schema, mode)?),
215+
})?;
216+
let hashrate_schema = pydantic_typed_dict_schema!(core_schema, "asic_rs.TuningTargetHashRate", {
217+
"type" => required(literal_schema(core_schema, &["hashrate"])?),
218+
"value" => required(<HashRate as PyPydanticType>::pydantic_schema(core_schema, mode)?),
219+
})?;
220+
let mode_schema = pydantic_typed_dict_schema!(core_schema, "asic_rs.TuningTargetMode", {
221+
"type" => required(literal_schema(core_schema, &["mode"])?),
222+
"value" => required(<MiningMode as PyPydanticType>::pydantic_schema(core_schema, mode)?),
223+
})?;
224+
let tagged_union = tagged_union_schema(
225+
core_schema,
226+
[
227+
("power", power_schema),
228+
("hashrate", hashrate_schema),
229+
("mode", mode_schema),
230+
],
231+
"type",
232+
Some("asic_rs.TuningTarget"),
233+
)?;
234+
if mode == PydanticSchemaMode::Serialization {
235+
return Ok(tagged_union);
236+
}
237+
let power_instance = core_schema.call_method1(
238+
"is_instance_schema",
239+
(core_schema.py().get_type::<TuningTargetPower>(),),
240+
)?;
241+
let hashrate_instance = core_schema.call_method1(
242+
"is_instance_schema",
243+
(core_schema.py().get_type::<TuningTargetHashRate>(),),
244+
)?;
245+
let mode_instance = core_schema.call_method1(
246+
"is_instance_schema",
247+
(core_schema.py().get_type::<TuningTargetMode>(),),
248+
)?;
249+
union_schema(
250+
core_schema,
251+
[
252+
power_instance,
253+
hashrate_instance,
254+
mode_instance,
255+
tagged_union,
256+
],
257+
)
258+
}
259+
260+
fn from_pydantic(value: &Bound<'_, PyAny>) -> PyResult<Self> {
261+
if let Ok(p) = value.extract::<PyRef<'_, TuningTargetPower>>() {
262+
return Ok(TuningTarget::Power(Power::from_watts(p.watts)));
263+
}
264+
if let Ok(hr) = value.extract::<PyRef<'_, TuningTargetHashRate>>() {
265+
return Ok(TuningTarget::HashRate(hr.hashrate.clone()));
266+
}
267+
if let Ok(m) = value.extract::<PyRef<'_, TuningTargetMode>>() {
268+
return Ok(TuningTarget::MiningMode(m.mode));
269+
}
270+
let type_str: String = get_required_field(value, "type")?.extract()?;
271+
let v = get_required_field(value, "value")?;
272+
match type_str.as_str() {
273+
"power" => Ok(TuningTarget::Power(
274+
<Power as PyPydanticType>::from_pydantic(&v)?,
275+
)),
276+
"hashrate" => Ok(TuningTarget::HashRate(
277+
<HashRate as PyPydanticType>::from_pydantic(&v)?,
278+
)),
279+
"mode" => Ok(TuningTarget::MiningMode(
280+
<MiningMode as PyPydanticType>::from_pydantic(&v)?,
281+
)),
282+
_ => Err(PyValueError::new_err(format!(
283+
"Unknown TuningTarget type '{type_str}', expected 'power', 'hashrate', or 'mode'"
284+
))),
285+
}
286+
}
287+
288+
fn to_pydantic_data(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
289+
use pyo3::types::{PyDict, PyDictMethods};
290+
let dict = PyDict::new(py);
291+
match self {
292+
TuningTarget::Power(p) => {
293+
dict.set_item("type", "power")?;
294+
dict.set_item("value", <Power as PyPydanticType>::to_pydantic_data(p, py)?)?;
295+
}
296+
TuningTarget::HashRate(hr) => {
297+
dict.set_item("type", "hashrate")?;
298+
dict.set_item(
299+
"value",
300+
<HashRate as PyPydanticType>::to_pydantic_data(hr, py)?,
301+
)?;
302+
}
303+
TuningTarget::MiningMode(m) => {
304+
dict.set_item("type", "mode")?;
305+
dict.set_item(
306+
"value",
307+
<MiningMode as PyPydanticType>::to_pydantic_data(m, py)?,
308+
)?;
309+
}
310+
}
311+
Ok(dict.into_any().unbind())
312+
}
313+
314+
fn to_pydantic_repr_value(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
315+
use pyo3::IntoPyObject as _;
316+
match self {
317+
TuningTarget::Power(p) => TuningTargetPower {
318+
watts: p.as_watts(),
319+
}
320+
.into_pyobject(py)
321+
.map(|b| b.into_any().unbind()),
322+
TuningTarget::HashRate(hr) => TuningTargetHashRate {
323+
hashrate: hr.clone(),
324+
}
325+
.into_pyobject(py)
326+
.map(|b| b.into_any().unbind()),
327+
TuningTarget::MiningMode(m) => TuningTargetMode { mode: *m }
328+
.into_pyobject(py)
329+
.map(|b| b.into_any().unbind()),
330+
}
331+
}
332+
}
333+
}

python/pyasic_rs/asic_rs.pyi

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/pyasic_rs/data.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pyasic_rs.asic_rs import MessageSeverity
77
from pyasic_rs.asic_rs import MiningMode
88
from pyasic_rs.asic_rs import PoolData, PoolGroupData, PoolScheme, PoolURL
9+
from pyasic_rs.asic_rs import TuningTargetMode, TuningTargetPower, TuningTargetHashRate
910

1011
__all__ = [
1112
"BoardData",
@@ -24,4 +25,7 @@
2425
"PoolGroupData",
2526
"PoolScheme",
2627
"PoolURL",
28+
"TuningTargetMode",
29+
"TuningTargetPower",
30+
"TuningTargetHashRate"
2731
]

src/python/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ mod asic_rs {
3838
device::{DeviceInfo, MinerHardware},
3939
fan::FanData,
4040
message::{MessageSeverity, MinerMessage},
41-
miner::MinerData,
41+
miner::{MinerData, TuningTargetHashRate, TuningTargetMode, TuningTargetPower},
4242
pool::{PoolData, PoolGroupData, PoolScheme, PoolURL},
4343
};
4444
}

0 commit comments

Comments
 (0)