update to PyO3 0.22, add 3.13 support

Co-authored-by: kotoriのねこ <minamiktr@outlook.com>
This commit is contained in:
luoshuijs 2024-12-09 15:05:35 +08:00 committed by GitHub
parent d8117b36b2
commit 68bef8ce3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 180 additions and 110 deletions

View File

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
with:
@ -43,7 +43,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
with:
@ -68,7 +68,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ]
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
with:
@ -109,7 +109,7 @@ jobs:
rust-toolchain: nightly
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist --interpreter 3.8 3.9 3.10 3.11 3.12
args: --release --out dist --interpreter 3.8 3.9 3.10 3.11 3.12 3.13
- name: Upload wheels
uses: actions/upload-artifact@v3
with:

View File

@ -24,7 +24,10 @@ name = "_python_genshin_artifact"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.20.3", features = ["anyhow"] }
# TODO it would be very nice to remove the "py-clone" feature as it can panic,
# but needs a bit of work to make sure it's not used in the codebase
# see https://pyo3.rs/v0.23.3/migration.html#pyclone-is-now-gated-behind-the-py-clone-feature
pyo3 = { version = "0.23", features = ["anyhow", "py-clone"] }
mona_wasm = { path = "genshin_artifact/mona_wasm" }
mona = { path = "genshin_artifact/mona_core" }
mona_generate = { path = "genshin_artifact/mona_generate" }
@ -32,7 +35,7 @@ num = "0.4"
serde="1.0"
serde_json = "1.0"
anyhow = "1.0"
pythonize = "0.20.0"
pythonize = "0.23"
bincode = "1.3.3"
[features]

View File

@ -0,0 +1,69 @@
# PyO3 v0.23.0 Upgrade
## Phase out the usage of GIL-refs
According to the PyO3 0.21.0 relase note,
> This release introduces a substantial new direction for PyO3's API. The `Bound<'py, T>` smart pointer type
> has been added that replaces "GIL Refs" such as `&'py PyAny` and `&'py PyList` with smart-pointer forms
> `Bound<'py, PyAny>` and `Bound<'py, PyList>`. This new smart pointer brings ownership out of PyO3's internals
> and into user control. This has been done for sake of both performance and soundness.
Thus, the usage of `.as_ref(py)` needs to be phased out and replaced by `.bind(py)`:
```diff
pub fn __repr__(&self, py: Python) -> PyResult<String> {
- let set_name = self.set_name.as_ref(py).to_str()?;
- let slot = self.slot.as_ref(py).to_str()?;
- let main_stat = self.main_stat.0.as_ref(py).to_str()?;
+ let set_name = self.set_name.bind(py).to_str()?;
+ let slot = self.slot.bind(py).to_str()?;
+ let main_stat = self.main_stat.0.bind(py).to_str()?;
let main_stat_value = self.main_stat.1;
```
## Use `Bound<T>` in method arguments and return type
Explicitly use `Bound<'py, T>` in the return type of `__dict__`:
```diff
#[getter]
- pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
+ pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
- let name_str = self.name.as_ref(py).to_str()?;
+ let name_str = self.name.bind(py).to_str()?;
dict.set_item("name", name_str)?;
if let Some(config) = &self.config {
- dict.set_item("config", config.as_ref(py))?;
+ dict.set_item("config", config.bind(py))?;
} else {
dict.set_item("config", py.None())?;
}
- Ok(dict.into())
+ Ok(dict)
}
```
Also apply `Bound<T>` to method argument:
```diff
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,7 +25,7 @@ use crate::applications::output::transformative_damage::PyTransformativeDamage;
import_exception!(json, JSONDecodeError);
#[pymodule]
-fn _python_genshin_artifact(py: Python<'_>, m: &PyModule) -> PyResult<()> {
+fn _python_genshin_artifact(py: Python<'_>, m: &Bound<PyModule>) -> PyResult<()> {
m.add("JSONDecodeError", py.get_type::<JSONDecodeError>())?;
m.add_function(wrap_pyfunction!(get_damage_analysis, m)?)?;
m.add_function(wrap_pyfunction!(get_transformative_damage, m)?)?;
```
## References
- [PyO3 Migration Guide](https://pyo3.rs/v0.23.0/migration.html#from-020-to-021)
- [pydantic-core#1222 - Upgrade to PyO3 0.21 beta](https://github.com/pydantic/pydantic-core/pull/1222/files#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542)
- [pydantic-core#1556 - Upgrade to PyO3 0.23 (minimal)](https://github.com/pydantic/pydantic-core/pull/1556/files)
- [pydantic-core#1450 - Upgrade to PyO3 0.23 head (WIP)](https://github.com/pydantic/pydantic-core/pull/1450/files)

View File

@ -18,6 +18,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
'Programming Language :: Python :: 3.13',
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",

View File

@ -43,7 +43,7 @@ pub fn get_damage_analysis(calculator_config: PyCalculatorConfig) -> PyResult<Py
let artifact_config_interface: Option<ArtifactConfigInterface> =
if let Some(artifact_config) = calculator_config.artifact_config {
Python::with_gil(|py| {
depythonize(artifact_config.as_ref(py))
depythonize(artifact_config.bind(py))
.map_err(|err| anyhow!("Failed to deserialize artifact config: {}", err))
})?
} else {
@ -117,7 +117,7 @@ pub fn get_transformative_damage(
let artifact_config_interface: Option<ArtifactConfigInterface> =
if let Some(artifact_config) = calculator_config.artifact_config {
Python::with_gil(|py| {
depythonize(artifact_config.as_ref(py))
depythonize(artifact_config.bind(py))
.map_err(|err| anyhow!("Failed to deserialize artifact config: {}", err))
})?
} else {

View File

@ -50,9 +50,9 @@ impl PyArtifact {
}
pub fn __repr__(&self, py: Python) -> PyResult<String> {
let set_name = self.set_name.as_ref(py).to_str()?;
let slot = self.slot.as_ref(py).to_str()?;
let main_stat = self.main_stat.0.as_ref(py).to_str()?;
let set_name = self.set_name.bind(py).to_str()?;
let slot = self.slot.bind(py).to_str()?;
let main_stat = self.main_stat.0.bind(py).to_str()?;
let main_stat_value = self.main_stat.1;
Ok(format!(
"PyArtifact(set_name='{}', slot='{}', level={}, star={}, main_stat=({}, {}), id={})",
@ -61,25 +61,25 @@ impl PyArtifact {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("set_name", self.set_name.as_ref(py))?;
dict.set_item("slot", self.slot.as_ref(py))?;
dict.set_item("set_name", self.set_name.bind(py))?;
dict.set_item("slot", self.slot.bind(py))?;
dict.set_item("level", self.level)?;
dict.set_item("star", self.star)?;
let sub_stats_pylist = PyList::new(
py,
self.sub_stats.iter().map(|(s, v)| {
let stat_str = s.as_ref(py).to_str().unwrap();
let stat_str = s.bind(py).to_str().unwrap();
(stat_str, *v)
}),
);
)?;
dict.set_item("sub_stats", sub_stats_pylist)?;
let main_stat_tuple = (self.main_stat.0.as_ref(py), self.main_stat.1);
let main_stat_tuple = (self.main_stat.0.bind(py), self.main_stat.1);
dict.set_item("main_stat", main_stat_tuple)?;
dict.set_item("id", self.id)?;
Ok(dict.into())
Ok(dict)
}
}
@ -88,7 +88,7 @@ impl TryInto<MonaArtifact> for PyArtifact {
fn try_into(self) -> Result<MonaArtifact, Self::Error> {
let name: ArtifactSetName = Python::with_gil(|py| {
let _string: &PyString = self.set_name.as_ref(py);
let _string: &Bound<'_, PyString> = self.set_name.bind(py);
depythonize(_string).map_err(|err| {
let serialized_data = format!("{:?}", _string);
anyhow!(
@ -100,7 +100,7 @@ impl TryInto<MonaArtifact> for PyArtifact {
})?;
let slot: ArtifactSlotName = Python::with_gil(|py| {
let _string: &PyString = self.slot.as_ref(py);
let _string: &Bound<'_, PyString> = self.slot.bind(py);
depythonize(_string).map_err(|err| {
let serialized_data = format!("{:?}", _string);
anyhow!(
@ -112,8 +112,8 @@ impl TryInto<MonaArtifact> for PyArtifact {
})?;
let main_stat_name: StatName = Python::with_gil(|py| {
let main_stat = self.main_stat.0.as_ref(py);
depythonize(self.main_stat.0.as_ref(py)).map_err(|err| {
let main_stat = self.main_stat.0.bind(py);
depythonize(self.main_stat.0.bind(py)).map_err(|err| {
let serialized_data = format!("{:?}", main_stat);
anyhow!(
"Failed to deserialize main stat into mona::artifacts::StatName: {}. Serialized data: \n{}",
@ -127,8 +127,8 @@ impl TryInto<MonaArtifact> for PyArtifact {
self.sub_stats
.iter()
.map(|s| {
let sub_stats = s.0.as_ref(py);
let name: Result<StatName, anyhow::Error> = depythonize(s.0.as_ref(py)).map_err(|err| {
let sub_stats = s.0.bind(py);
let name: Result<StatName, anyhow::Error> = depythonize(s.0.bind(py)).map_err(|err| {
let serialized_data = format!("{:?}", sub_stats);
anyhow!(
"Failed to deserialize sub stats into mona::artifacts::StatName: {}. Serialized data: \n{}",
@ -166,7 +166,8 @@ mod tests {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let module = PyModule::import(py, "python_genshin_artifact.enka.artifacts")?;
let artifacts_name_map = module.getattr("artifacts_name_map")?.downcast::<PyDict>()?;
let artifacts_name_map = module.getattr("artifacts_name_map")?;
let artifacts_name_map = artifacts_name_map.downcast::<PyDict>()?;
for (_, value) in artifacts_name_map.iter() {
let artifacts_name_str = value.extract::<String>()?;
let res: Result<ArtifactSetName, anyhow::Error> = depythonize(&value).context(

View File

@ -21,14 +21,15 @@ pub struct PyBuffInterface {
#[pymethods]
impl PyBuffInterface {
#[new]
#[pyo3(signature = (name, config=None))]
pub fn py_new(name: Py<PyString>, config: Option<Py<PyDict>>) -> PyResult<Self> {
Ok(Self { name, config })
}
pub fn __repr__(&self, py: Python) -> PyResult<String> {
let name = self.name.as_ref(py).to_str()?;
let name = self.name.bind(py).to_str()?;
let config_repr = match &self.config {
Some(config) => config.as_ref(py).repr()?.to_str()?.to_string(),
Some(config) => config.bind(py).repr()?.to_str()?.to_string(),
None => "None".to_string(),
};
Ok(format!(
@ -38,16 +39,16 @@ impl PyBuffInterface {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
let name_str = self.name.as_ref(py).to_str()?;
let name_str = self.name.bind(py).to_str()?;
dict.set_item("name", name_str)?;
if let Some(config) = &self.config {
dict.set_item("config", config.as_ref(py))?;
dict.set_item("config", config.bind(py))?;
} else {
dict.set_item("config", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}
@ -56,7 +57,7 @@ impl TryInto<MonaBuffInterface> for PyBuffInterface {
fn try_into(self) -> Result<MonaBuffInterface, Self::Error> {
let name: BuffName = Python::with_gil(|py| {
let _string: &PyString = self.name.as_ref(py);
let _string: &Bound<'_, PyString> = self.name.bind(py);
depythonize(_string).map_err(|err| {
let serialized_data = format!("{:?}", _string);
anyhow!(
@ -69,7 +70,7 @@ impl TryInto<MonaBuffInterface> for PyBuffInterface {
let config: BuffConfig = if let Some(value) = self.config {
Python::with_gil(|py| {
let _dict: &PyDict = value.as_ref(py);
let _dict: &Bound<'_, PyDict> = value.bind(py);
depythonize(_dict).map_err(|err| {
let serialized_data = format!("{:?}", _dict);
anyhow!(

View File

@ -52,7 +52,7 @@ impl PyCalculatorConfig {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("character", self.character.__dict__(py)?)?;
dict.set_item("weapon", self.weapon.__dict__(py)?)?;
@ -60,15 +60,15 @@ impl PyCalculatorConfig {
.buffs
.iter()
.map(|b| b.__dict__(py))
.collect::<Result<Vec<PyObject>, PyErr>>()?;
dict.set_item("buffs", PyList::new(py, buffs))?;
.collect::<Result<Vec<Bound<PyDict>>, PyErr>>()?;
dict.set_item("buffs", PyList::new(py, buffs)?)?;
let artifacts = self
.artifacts
.iter()
.map(|ar| ar.__dict__(py))
.collect::<Result<Vec<PyObject>, PyErr>>()?;
dict.set_item("artifacts", PyList::new(py, artifacts))?;
if let Some(artifact_config) = self.artifact_config.as_ref().map(|c| c.as_ref(py)) {
.collect::<Result<Vec<Bound<PyDict>>, PyErr>>()?;
dict.set_item("artifacts", PyList::new(py, artifacts)?)?;
if let Some(artifact_config) = self.artifact_config.as_ref().map(|c| c.bind(py)) {
dict.set_item("artifact_config", artifact_config)?;
} else {
dict.set_item("artifact_config", py.None())?;
@ -79,6 +79,6 @@ impl PyCalculatorConfig {
} else {
dict.set_item("enemy", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}

View File

@ -32,6 +32,7 @@ pub struct PyCharacterInterface {
#[pymethods]
impl PyCharacterInterface {
#[new]
#[pyo3(signature = (name, level, ascend, constellation, skill1, skill2, skill3, params=None))]
pub fn py_new(
name: String,
level: usize,
@ -66,7 +67,7 @@ impl PyCharacterInterface {
))
}
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("name", &self.name)?;
@ -83,7 +84,7 @@ impl PyCharacterInterface {
dict.set_item("params", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}
@ -95,7 +96,7 @@ impl TryInto<MonaCharacterInterface> for PyCharacterInterface {
.context("Failed to name params into mona::character::CharacterName")?;
let params: CharacterConfig = if let Some(value) = self.params {
Python::with_gil(|py| {
let _dict: &PyDict = value.as_ref(py);
let _dict: &Bound<'_, PyDict> = value.bind(py);
depythonize(_dict).map_err(|err| {
let serialized_data = format!("{:?}", _dict);
anyhow!(
@ -158,13 +159,9 @@ mod tests {
match &py_character_interface.params {
Some(value) => {
let py_dict = value.as_ref(py);
let hutao_dict = py_dict
.get_item("HuTao")
.unwrap()
.unwrap()
.downcast::<PyDict>()
.unwrap();
let py_dict = value.bind(py);
let hutao_dict = py_dict.get_item("HuTao").unwrap().unwrap();
let hutao_dict = hutao_dict.downcast::<PyDict>().unwrap();
assert_eq!(
hutao_dict
.get_item("le_50")
@ -211,7 +208,8 @@ mod tests {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let module = PyModule::import(py, "python_genshin_artifact.enka.characters")?;
let characters_map = module.getattr("characters_map")?.downcast::<PyDict>()?;
let characters_map = module.getattr("characters_map")?;
let characters_map = characters_map.downcast::<PyDict>()?;
for (_, value) in characters_map.iter() {
let character_name_str = value.extract::<String>()?;
let res = CharacterName::from_str(&character_name_str).context(format!(

View File

@ -69,7 +69,7 @@ impl PyEnemyInterface {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("level", self.level)?;
dict.set_item("electro_res", self.electro_res)?;
@ -80,7 +80,7 @@ impl PyEnemyInterface {
dict.set_item("anemo_res", self.anemo_res)?;
dict.set_item("dendro_res", self.dendro_res)?;
dict.set_item("physical_res", self.physical_res)?;
Ok(dict.into())
Ok(dict)
}
}

View File

@ -18,6 +18,7 @@ pub struct PySkillInterface {
#[pymethods]
impl PySkillInterface {
#[new]
#[pyo3(signature = (index, config=None))]
fn new(index: usize, config: Option<Py<PyDict>>) -> PyResult<Self> {
Ok(Self { index, config })
}
@ -29,15 +30,15 @@ impl PySkillInterface {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("index", self.index)?;
if let Some(config) = &self.config {
dict.set_item("config", config.as_ref(py))?;
dict.set_item("config", config.bind(py))?;
} else {
dict.set_item("config", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}
@ -47,7 +48,7 @@ impl TryInto<MonaSkillInterface> for PySkillInterface {
fn try_into(self) -> Result<MonaSkillInterface, Self::Error> {
let config: CharacterSkillConfig = if let Some(value) = self.config {
Python::with_gil(|py| {
let _dict: &PyDict = value.as_ref(py);
let _dict: &Bound<'_, PyDict> = value.bind(py);
depythonize(_dict).map_err(|err| {
let serialized_data = format!("{:?}", _dict);
anyhow!("Failed to deserialize config into mona::character::skill_config::CharacterSkillConfig: {}. Serialized data: \n{}", err, serialized_data)

View File

@ -25,6 +25,7 @@ pub struct PyWeaponInterface {
#[pymethods]
impl PyWeaponInterface {
#[new]
#[pyo3(signature = (name, level, ascend, refine, params=None))]
pub fn py_new(
name: Py<PyString>,
level: i32,
@ -42,9 +43,9 @@ impl PyWeaponInterface {
}
pub fn __repr__(&self, py: Python) -> PyResult<String> {
let name = self.name.as_ref(py).to_str()?;
let name = self.name.bind(py).to_str()?;
let params_repr = match &self.params {
Some(params) => params.as_ref(py).repr()?.to_str()?.to_string(),
Some(params) => params.bind(py).repr()?.to_str()?.to_string(),
None => "None".to_string(),
};
@ -55,18 +56,18 @@ impl PyWeaponInterface {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("name", self.name.as_ref(py))?;
dict.set_item("name", self.name.bind(py))?;
dict.set_item("level", self.level)?;
dict.set_item("ascend", self.ascend)?;
dict.set_item("refine", self.refine)?;
if let Some(params) = &self.params {
dict.set_item("params", params.as_ref(py))?;
dict.set_item("params", params.bind(py))?;
} else {
dict.set_item("params", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}
@ -75,7 +76,7 @@ impl TryInto<MonaWeaponInterface> for PyWeaponInterface {
fn try_into(self) -> Result<MonaWeaponInterface, Self::Error> {
let name: WeaponName = Python::with_gil(|py| {
let _string: &PyString = self.name.as_ref(py);
let _string: &Bound<'_, PyString> = self.name.bind(py);
depythonize(_string).map_err(|err| {
let serialized_data = format!("{:?}", _string);
anyhow!(
@ -88,7 +89,7 @@ impl TryInto<MonaWeaponInterface> for PyWeaponInterface {
let params: WeaponConfig = if let Some(value) = self.params {
Python::with_gil(|py| {
let _dict: &PyDict = value.as_ref(py);
let _dict: &Bound<'_, PyDict> = value.bind(py);
depythonize(_dict).map_err(|err| {
let serialized_data = format!("{:?}", _dict);
anyhow!(
@ -139,23 +140,16 @@ mod tests {
params: Some(Py::from(params_dict)),
};
assert_eq!(
py_weapon_interface.name.as_ref(py).to_string(),
"StaffOfHoma"
);
assert_eq!(py_weapon_interface.name.bind(py).to_string(), "StaffOfHoma");
assert_eq!(py_weapon_interface.level, 90);
assert!(py_weapon_interface.ascend);
assert_eq!(py_weapon_interface.refine, 5);
match &py_weapon_interface.params {
Some(value) => {
let py_dict = value.as_ref(py);
let params_dict = py_dict
.get_item("StaffOfHoma")
.unwrap()
.unwrap()
.downcast::<PyDict>()
.unwrap();
let py_dict = value.bind(py);
let params_dict = py_dict.get_item("StaffOfHoma").unwrap().unwrap();
let params_dict = params_dict.downcast::<PyDict>().unwrap();
assert_eq!(
params_dict
.get_item("be50_rate")
@ -223,7 +217,8 @@ mod tests {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let module = PyModule::import(py, "python_genshin_artifact.enka.weapon")?;
let weapon_name_map = module.getattr("weapon_name_map")?.downcast::<PyDict>()?;
let weapon_name_map = module.getattr("weapon_name_map")?;
let weapon_name_map = weapon_name_map.downcast::<PyDict>()?;
for (_, value) in weapon_name_map.iter() {
let weapon_name_str = value.extract::<String>()?;
let res: Result<WeaponName, anyhow::Error> = depythonize(&value)

View File

@ -73,11 +73,12 @@ pub struct PyDamageAnalysis {
#[pymethods]
impl PyDamageAnalysis {
#[getter]
fn __dict__(&self, py: Python) -> PyResult<PyObject> { // skipcq: RS-R1000
fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
// skipcq: RS-R1000
let dict = PyDict::new(py);
fn insert_hashmap(
dict: &PyDict,
dict: &Bound<PyDict>,
py: Python,
key: &str,
hashmap: &HashMap<String, f64>,
@ -90,27 +91,27 @@ impl PyDamageAnalysis {
Ok(())
}
insert_hashmap(dict, py, "atk", &self.atk)?;
insert_hashmap(dict, py, "atk_ratio", &self.atk_ratio)?;
insert_hashmap(dict, py, "hp", &self.hp)?;
insert_hashmap(dict, py, "hp_ratio", &self.hp_ratio)?;
insert_hashmap(dict, py, "defense", &self.def)?;
insert_hashmap(dict, py, "def_ratio", &self.def_ratio)?;
insert_hashmap(dict, py, "em", &self.em)?;
insert_hashmap(dict, py, "em_ratio", &self.em_ratio)?;
insert_hashmap(dict, py, "extra_damage", &self.extra_damage)?;
insert_hashmap(dict, py, "bonus", &self.bonus)?;
insert_hashmap(dict, py, "critical", &self.critical)?;
insert_hashmap(dict, py, "critical_damage", &self.critical_damage)?;
insert_hashmap(dict, py, "melt_enhance", &self.melt_enhance)?;
insert_hashmap(dict, py, "vaporize_enhance", &self.vaporize_enhance)?;
insert_hashmap(dict, py, "healing_bonus", &self.healing_bonus)?;
insert_hashmap(dict, py, "shield_strength", &self.shield_strength)?;
insert_hashmap(dict, py, "spread_compose", &self.spread_compose)?;
insert_hashmap(dict, py, "aggravate_compose", &self.aggravate_compose)?;
insert_hashmap(dict, py, "def_minus", &self.def_minus)?;
insert_hashmap(dict, py, "def_penetration", &self.def_penetration)?;
insert_hashmap(dict, py, "res_minus", &self.res_minus)?;
insert_hashmap(&dict, py, "atk", &self.atk)?;
insert_hashmap(&dict, py, "atk_ratio", &self.atk_ratio)?;
insert_hashmap(&dict, py, "hp", &self.hp)?;
insert_hashmap(&dict, py, "hp_ratio", &self.hp_ratio)?;
insert_hashmap(&dict, py, "defense", &self.def)?;
insert_hashmap(&dict, py, "def_ratio", &self.def_ratio)?;
insert_hashmap(&dict, py, "em", &self.em)?;
insert_hashmap(&dict, py, "em_ratio", &self.em_ratio)?;
insert_hashmap(&dict, py, "extra_damage", &self.extra_damage)?;
insert_hashmap(&dict, py, "bonus", &self.bonus)?;
insert_hashmap(&dict, py, "critical", &self.critical)?;
insert_hashmap(&dict, py, "critical_damage", &self.critical_damage)?;
insert_hashmap(&dict, py, "melt_enhance", &self.melt_enhance)?;
insert_hashmap(&dict, py, "vaporize_enhance", &self.vaporize_enhance)?;
insert_hashmap(&dict, py, "healing_bonus", &self.healing_bonus)?;
insert_hashmap(&dict, py, "shield_strength", &self.shield_strength)?;
insert_hashmap(&dict, py, "spread_compose", &self.spread_compose)?;
insert_hashmap(&dict, py, "aggravate_compose", &self.aggravate_compose)?;
insert_hashmap(&dict, py, "def_minus", &self.def_minus)?;
insert_hashmap(&dict, py, "def_penetration", &self.def_penetration)?;
insert_hashmap(&dict, py, "res_minus", &self.res_minus)?;
dict.set_item("element", &self.element)?;
dict.set_item("is_heal", self.is_heal)?;
@ -143,7 +144,7 @@ impl PyDamageAnalysis {
dict.set_item("aggravate", py.None())?;
}
Ok(dict.into())
Ok(dict)
}
}

View File

@ -2,8 +2,8 @@ use mona::damage::damage_result::DamageResult as MonaDamageResult;
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyDict};
use serde::{Serialize, Deserialize};
use bincode::{serialize, deserialize};
use bincode::{deserialize, serialize};
use serde::{Deserialize, Serialize};
#[pyclass(module = "python_genshin_artifact", name = "DamageResult")]
#[derive(Clone, Deserialize, Serialize)]
@ -47,22 +47,22 @@ impl PyDamageResult {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("critical", self.critical)?;
dict.set_item("non_critical", self.non_critical)?;
dict.set_item("expectation", self.expectation)?;
dict.set_item("is_heal", self.is_heal)?;
dict.set_item("is_shield", self.is_shield)?;
Ok(dict.into())
Ok(dict)
}
pub fn __setstate__(&mut self, state: &PyBytes) -> PyResult<()> {
pub fn __setstate__(&mut self, state: Bound<'_, PyBytes>) -> PyResult<()> {
*self = deserialize(state.as_bytes()).unwrap();
Ok(())
}
pub fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult<&'py PyBytes> {
pub fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &serialize(&self).unwrap()))
}

View File

@ -69,7 +69,7 @@ impl PyTransformativeDamage {
}
#[getter]
pub fn __dict__(&self, py: Python) -> PyResult<PyObject> {
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let dict = PyDict::new(py);
dict.set_item("swirl_cryo", self.swirl_cryo)?;
dict.set_item("swirl_hydro", self.swirl_hydro)?;
@ -84,7 +84,7 @@ impl PyTransformativeDamage {
dict.set_item("burgeon", self.burgeon)?;
dict.set_item("burning", self.burning)?;
dict.set_item("crystallize", self.crystallize)?;
Ok(dict.into())
Ok(dict)
}
}

View File

@ -25,7 +25,7 @@ use crate::applications::output::transformative_damage::PyTransformativeDamage;
import_exception!(json, JSONDecodeError);
#[pymodule]
fn _python_genshin_artifact(py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn _python_genshin_artifact(py: Python<'_>, m: &Bound<PyModule>) -> PyResult<()> {
m.add("JSONDecodeError", py.get_type::<JSONDecodeError>())?;
m.add_function(wrap_pyfunction!(get_damage_analysis, m)?)?;
m.add_function(wrap_pyfunction!(get_transformative_damage, m)?)?;