microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7421e7dd1015dcbd940bf843d33583470de580ea

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/src/generic_estimator/utils.rs

164lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::ops::Deref;
5
6use pyo3::{
7 Bound, IntoPyObject, PyAny, PyErr, PyResult, Python,
8 exceptions::PyTypeError,
9 types::{
10 PyAnyMethods, PyBool, PyDict, PyDictMethods, PyFloat, PyInt, PyList, PyListMethods, PyNone,
11 PyString, PyTypeMethods,
12 },
13};
14use serde::{Serialize, ser::SerializeMap};
15use serde_json::{Map, Value, json};
16
17/// Converts a JSON value to a Python object, handling various types such as
18/// `null`, `number`, `string`, `boolean`, `array`, and `object`.
19fn json_value_to_python_object<'py>(py: Python<'py>, value: &Value) -> PyResult<Bound<'py, PyAny>> {
20 match value {
21 Value::Null => Ok(PyNone::get(py).to_owned().into_any()),
22 Value::Number(n) => {
23 if let Some(int) = n.as_i64() {
24 Ok(int.into_pyobject(py)?.into_any())
25 } else if let Some(float) = n.as_f64() {
26 Ok(float.into_pyobject(py)?.into_any())
27 } else {
28 Err(PyErr::new::<PyTypeError, _>(format!("cannot convert {n}")))
29 }
30 }
31 Value::String(s) => Ok(PyString::new(py, s).into_any()),
32 &Value::Bool(b) => Ok(b.into_pyobject(py)?.to_owned().into_any()),
33 Value::Array(elements) => {
34 let list = PyList::empty(py);
35
36 for element in elements {
37 list.append(json_value_to_python_object(py, element)?)?;
38 }
39
40 Ok(list.into_any())
41 }
42 Value::Object(map) => Ok(json_map_to_python_dict(py, map)?.into_any()),
43 }
44}
45
46/// Converts a JSON map to a Python dictionary.
47pub(crate) fn json_map_to_python_dict<'py>(
48 py: Python<'py>,
49 map: &Map<String, Value>,
50) -> PyResult<Bound<'py, PyDict>> {
51 let dict = PyDict::new(py);
52 for (key, value) in map {
53 dict.set_item(key, json_value_to_python_object(py, value)?)?;
54 }
55 Ok(dict)
56}
57
58/// Converts a Python object to a JSON value, handling various types such as
59/// `None`, `int`, `float`, `bool`, `str`, `list`, and `dict`.
60pub(crate) fn python_object_to_json_value(value: &Bound<'_, PyAny>) -> Option<Value> {
61 if value.is_none() {
62 Some(Value::Null)
63 } else if let Ok(n) = value.downcast_exact::<PyInt>() {
64 Some(json!(n.extract::<i64>().expect("n is PyInt")))
65 } else if let Ok(n) = value.downcast_exact::<PyFloat>() {
66 Some(json!(n.extract::<f64>().expect("n is PyFloat")))
67 } else if let Ok(b) = value.downcast_exact::<PyBool>() {
68 Some(json!(b.extract::<bool>().expect("b is PyBool")))
69 } else if let Ok(s) = value.downcast_exact::<PyString>() {
70 Some(json!(s.extract::<String>().expect("s is PyString")))
71 } else if let Ok(l) = value.downcast_exact::<PyList>() {
72 let values: Vec<_> = l
73 .iter()
74 .map(|v| python_object_to_json_value(&v))
75 .collect::<Option<_>>()?;
76 Some(Value::Array(values))
77 } else if let Ok(d) = value.downcast_exact::<PyDict>() {
78 Some(Value::Object(python_dict_to_json_map(d)?))
79 } else {
80 None
81 }
82}
83
84/// Converts a Python dictionary to a JSON map.
85pub fn python_dict_to_json_map(value: &Bound<'_, PyDict>) -> Option<Map<String, Value>> {
86 let mut map: Map<String, Value> = Map::new();
87 for (key, value) in value.iter() {
88 map.insert(
89 key.extract::<String>().ok()?,
90 python_object_to_json_value(&value)?,
91 );
92 }
93 Some(map)
94}
95
96/// A wrapper around a Python instance that can be serialized in order to embed
97/// its value into the returned resource estimates.
98#[derive(Clone, Debug)]
99pub struct SerializableBound<'py>(pub Bound<'py, PyAny>);
100
101impl<'py> Deref for SerializableBound<'py> {
102 type Target = Bound<'py, PyAny>;
103
104 fn deref(&self) -> &Self::Target {
105 &self.0
106 }
107}
108
109impl Serialize for SerializableBound<'_> {
110 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111 where
112 S: serde::Serializer,
113 {
114 if let Ok(dict) = self.downcast::<PyDict>() {
115 let mut map = serializer.serialize_map(Some(dict.len()))?;
116 for (key, value) in dict.iter() {
117 map.serialize_key(&SerializableBound(key))?;
118 map.serialize_value(&SerializableBound(value))?;
119 }
120 map.end()
121 } else if let Ok(number) = self.downcast::<PyInt>() {
122 serializer.serialize_i64(number.extract().expect("number is PyInt"))
123 } else {
124 serializer.serialize_str(&self.to_string())
125 }
126 }
127}
128
129/// Extracts a method from a Python instance and checks if it is callable
130pub fn extract_and_check_method<'py>(
131 instance: &Bound<'py, PyAny>,
132 method_name: &str,
133) -> PyResult<Bound<'py, PyAny>> {
134 let member = instance.getattr(method_name)?;
135 if !member.is_callable() {
136 return Err(PyTypeError::new_err(format!(
137 "Method '{}' is not callable on the instance of type '{}'",
138 method_name,
139 instance.get_type().name()?
140 )));
141 }
142 Ok(member)
143}
144
145/// Attempts to extract a method from a Python instance and checks if it is
146/// callable
147pub fn maybe_extract_and_check_method<'py>(
148 instance: &Bound<'py, PyAny>,
149 method_name: &str,
150) -> PyResult<Option<Bound<'py, PyAny>>> {
151 if !instance.hasattr(method_name)? {
152 return Ok(None);
153 }
154
155 let member = instance.getattr(method_name)?;
156 if !member.is_callable() {
157 return Err(PyTypeError::new_err(format!(
158 "Method '{}' is not callable on the instance of type '{}'",
159 method_name,
160 instance.get_type().name()?
161 )));
162 }
163 Ok(Some(member))
164}
165