diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab9cf96..090ba72 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index eb76a63..fee7e26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/docs/PyO3_v0.23.0_upgrade.md b/docs/PyO3_v0.23.0_upgrade.md new file mode 100644 index 0000000..4a5ecc8 --- /dev/null +++ b/docs/PyO3_v0.23.0_upgrade.md @@ -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 { +- 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` 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 { ++ pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { + 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` 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) -> PyResult<()> { + m.add("JSONDecodeError", py.get_type::())?; + 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) diff --git a/pyproject.toml b/pyproject.toml index 36c32ea..aecd79d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", diff --git a/src/applications/analysis.rs b/src/applications/analysis.rs index 13e4569..ef6cfdb 100644 --- a/src/applications/analysis.rs +++ b/src/applications/analysis.rs @@ -43,7 +43,7 @@ pub fn get_damage_analysis(calculator_config: PyCalculatorConfig) -> PyResult = 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 = 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 { diff --git a/src/applications/input/artifact.rs b/src/applications/input/artifact.rs index 4283ac1..42cc194 100644 --- a/src/applications/input/artifact.rs +++ b/src/applications/input/artifact.rs @@ -50,9 +50,9 @@ impl PyArtifact { } pub fn __repr__(&self, py: Python) -> PyResult { - 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 { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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 for PyArtifact { fn try_into(self) -> Result { 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 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 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 for PyArtifact { self.sub_stats .iter() .map(|s| { - let sub_stats = s.0.as_ref(py); - let name: Result = depythonize(s.0.as_ref(py)).map_err(|err| { + let sub_stats = s.0.bind(py); + let name: Result = 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::()?; + let artifacts_name_map = module.getattr("artifacts_name_map")?; + let artifacts_name_map = artifacts_name_map.downcast::()?; for (_, value) in artifacts_name_map.iter() { let artifacts_name_str = value.extract::()?; let res: Result = depythonize(&value).context( diff --git a/src/applications/input/buff.rs b/src/applications/input/buff.rs index 0fb0c77..c18e900 100644 --- a/src/applications/input/buff.rs +++ b/src/applications/input/buff.rs @@ -21,14 +21,15 @@ pub struct PyBuffInterface { #[pymethods] impl PyBuffInterface { #[new] + #[pyo3(signature = (name, config=None))] pub fn py_new(name: Py, config: Option>) -> PyResult { Ok(Self { name, config }) } pub fn __repr__(&self, py: Python) -> PyResult { - 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 { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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 for PyBuffInterface { fn try_into(self) -> Result { 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 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!( diff --git a/src/applications/input/calculator.rs b/src/applications/input/calculator.rs index dcf25d9..f1c2ccf 100644 --- a/src/applications/input/calculator.rs +++ b/src/applications/input/calculator.rs @@ -52,7 +52,7 @@ impl PyCalculatorConfig { } #[getter] - pub fn __dict__(&self, py: Python) -> PyResult { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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::, PyErr>>()?; - dict.set_item("buffs", PyList::new(py, buffs))?; + .collect::>, PyErr>>()?; + dict.set_item("buffs", PyList::new(py, buffs)?)?; let artifacts = self .artifacts .iter() .map(|ar| ar.__dict__(py)) - .collect::, 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::>, 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) } } diff --git a/src/applications/input/character.rs b/src/applications/input/character.rs index b136d76..c2b669a 100644 --- a/src/applications/input/character.rs +++ b/src/applications/input/character.rs @@ -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 { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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 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::() - .unwrap(); + let py_dict = value.bind(py); + let hutao_dict = py_dict.get_item("HuTao").unwrap().unwrap(); + let hutao_dict = hutao_dict.downcast::().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::()?; + let characters_map = module.getattr("characters_map")?; + let characters_map = characters_map.downcast::()?; for (_, value) in characters_map.iter() { let character_name_str = value.extract::()?; let res = CharacterName::from_str(&character_name_str).context(format!( diff --git a/src/applications/input/enemy.rs b/src/applications/input/enemy.rs index 3267b71..2431db6 100644 --- a/src/applications/input/enemy.rs +++ b/src/applications/input/enemy.rs @@ -69,7 +69,7 @@ impl PyEnemyInterface { } #[getter] - pub fn __dict__(&self, py: Python) -> PyResult { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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) } } diff --git a/src/applications/input/skill.rs b/src/applications/input/skill.rs index d7f9e3b..6b630d2 100644 --- a/src/applications/input/skill.rs +++ b/src/applications/input/skill.rs @@ -18,6 +18,7 @@ pub struct PySkillInterface { #[pymethods] impl PySkillInterface { #[new] + #[pyo3(signature = (index, config=None))] fn new(index: usize, config: Option>) -> PyResult { Ok(Self { index, config }) } @@ -29,15 +30,15 @@ impl PySkillInterface { } #[getter] - pub fn __dict__(&self, py: Python) -> PyResult { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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 for PySkillInterface { fn try_into(self) -> Result { 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) diff --git a/src/applications/input/weapon.rs b/src/applications/input/weapon.rs index 403d0db..dbde1bc 100644 --- a/src/applications/input/weapon.rs +++ b/src/applications/input/weapon.rs @@ -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, level: i32, @@ -42,9 +43,9 @@ impl PyWeaponInterface { } pub fn __repr__(&self, py: Python) -> PyResult { - 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 { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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 for PyWeaponInterface { fn try_into(self) -> Result { 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 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::() - .unwrap(); + let py_dict = value.bind(py); + let params_dict = py_dict.get_item("StaffOfHoma").unwrap().unwrap(); + let params_dict = params_dict.downcast::().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::()?; + let weapon_name_map = module.getattr("weapon_name_map")?; + let weapon_name_map = weapon_name_map.downcast::()?; for (_, value) in weapon_name_map.iter() { let weapon_name_str = value.extract::()?; let res: Result = depythonize(&value) diff --git a/src/applications/output/damage_analysis.rs b/src/applications/output/damage_analysis.rs index 7cf1678..3ca390f 100644 --- a/src/applications/output/damage_analysis.rs +++ b/src/applications/output/damage_analysis.rs @@ -73,11 +73,12 @@ pub struct PyDamageAnalysis { #[pymethods] impl PyDamageAnalysis { #[getter] - fn __dict__(&self, py: Python) -> PyResult { // skipcq: RS-R1000 + fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { + // skipcq: RS-R1000 let dict = PyDict::new(py); fn insert_hashmap( - dict: &PyDict, + dict: &Bound, py: Python, key: &str, hashmap: &HashMap, @@ -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) } } diff --git a/src/applications/output/damage_result.rs b/src/applications/output/damage_result.rs index 049cdf7..4716d86 100644 --- a/src/applications/output/damage_result.rs +++ b/src/applications/output/damage_result.rs @@ -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 { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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> { Ok(PyBytes::new(py, &serialize(&self).unwrap())) } diff --git a/src/applications/output/transformative_damage.rs b/src/applications/output/transformative_damage.rs index 11c7e01..a08eaa7 100644 --- a/src/applications/output/transformative_damage.rs +++ b/src/applications/output/transformative_damage.rs @@ -69,7 +69,7 @@ impl PyTransformativeDamage { } #[getter] - pub fn __dict__(&self, py: Python) -> PyResult { + pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { 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) } } diff --git a/src/lib.rs b/src/lib.rs index bf7354d..58f9317 100644 --- 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) -> PyResult<()> { m.add("JSONDecodeError", py.get_type::())?; m.add_function(wrap_pyfunction!(get_damage_analysis, m)?)?; m.add_function(wrap_pyfunction!(get_transformative_damage, m)?)?;