From 76836be002a90c06d1213f6f37f3fe1144f28695 Mon Sep 17 00:00:00 2001 From: LuoShui Date: Mon, 9 Dec 2024 08:06:02 +0800 Subject: [PATCH 1/8] :bug: try to fix --- Cargo.toml | 4 +- src/applications/input/artifact.rs | 54 +++++++++++----------- src/applications/input/buff.rs | 24 +++++----- src/applications/input/calculator.rs | 8 ++-- src/applications/input/character.rs | 14 +++--- src/applications/input/skill.rs | 11 ++--- src/applications/input/weapon.rs | 27 ++++++----- src/applications/output/damage_analysis.rs | 44 +++++++++--------- src/applications/output/damage_result.rs | 4 +- 9 files changed, 92 insertions(+), 98 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb76a63..4d50425 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ name = "_python_genshin_artifact" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.20.3", features = ["anyhow"] } +pyo3 = { version = "0.23", features = ["anyhow"] } mona_wasm = { path = "genshin_artifact/mona_wasm" } mona = { path = "genshin_artifact/mona_core" } mona_generate = { path = "genshin_artifact/mona_generate" } @@ -32,7 +32,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/src/applications/input/artifact.rs b/src/applications/input/artifact.rs index 4283ac1..5db5daa 100644 --- a/src/applications/input/artifact.rs +++ b/src/applications/input/artifact.rs @@ -11,17 +11,17 @@ use mona::common::StatName; #[derive(Clone)] pub struct PyArtifact { #[pyo3(get, set)] - pub set_name: Py, + pub set_name: String, #[pyo3(get, set)] - pub slot: Py, + pub slot: String, #[pyo3(get, set)] pub level: i32, #[pyo3(get, set)] pub star: i32, #[pyo3(get, set)] - pub sub_stats: Vec<(Py, f64)>, + pub sub_stats: Vec<(String, f64)>, #[pyo3(get, set)] - pub main_stat: (Py, f64), + pub main_stat: (String, f64), #[pyo3(get, set)] pub id: u64, } @@ -30,12 +30,12 @@ pub struct PyArtifact { impl PyArtifact { #[new] pub fn py_new( - set_name: Py, - slot: Py, + set_name: String, + slot: String, level: i32, star: i32, - sub_stats: Vec<(Py, f64)>, - main_stat: (Py, f64), + sub_stats: Vec<(String, f64)>, + main_stat: (String, f64), id: u64, ) -> PyResult { Ok(Self { @@ -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.as_str(); + let slot = self.slot.as_str()?; + let main_stat = self.main_stat.0.as_str()?; let main_stat_value = self.main_stat.1; Ok(format!( "PyArtifact(set_name='{}', slot='{}', level={}, star={}, main_stat=({}, {}), id={})", @@ -63,19 +63,19 @@ impl PyArtifact { #[getter] pub fn __dict__(&self, py: Python) -> 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.as_ref())?; + dict.set_item("slot", self.slot.as_ref())?; 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.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); + dict.set_item("sub_stats", &sub_stats_pylist)?; + let main_stat_tuple = (self.main_stat.0.as_ref(), self.main_stat.1); dict.set_item("main_stat", main_stat_tuple)?; dict.set_item("id", self.id)?; @@ -88,9 +88,9 @@ 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); - depythonize(_string).map_err(|err| { - let serialized_data = format!("{:?}", _string); + let _string = PyString::new(py, &self.set_name); + depythonize(&_string).map_err(|err| { + let serialized_data = format!("{:?}", self.set_name); anyhow!( "Failed to deserialize name into mona::artifacts::ArtifactSetName: {}. Serialized data: \n{}", err, @@ -100,9 +100,9 @@ impl TryInto for PyArtifact { })?; let slot: ArtifactSlotName = Python::with_gil(|py| { - let _string: &PyString = self.slot.as_ref(py); - depythonize(_string).map_err(|err| { - let serialized_data = format!("{:?}", _string); + let _string = PyString::new(py, &self.slot); + depythonize(&_string).map_err(|err| { + let serialized_data = format!("{:?}", &_string); anyhow!( "Failed to deserialize slot name into mona::artifacts::ArtifactSlotName: {}. Serialized data: \n{}", err, @@ -112,9 +112,9 @@ 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 serialized_data = format!("{:?}", main_stat); + let _string = PyString::new(py, &self.main_stat.0); + depythonize(&_string).map_err(|err| { + let serialized_data = format!("{:?}", self.main_stat.0); anyhow!( "Failed to deserialize main stat into mona::artifacts::StatName: {}. Serialized data: \n{}", err, @@ -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.as_ref(); + let name: Result = depythonize(s.0.as_ref()).map_err(|err| { let serialized_data = format!("{:?}", sub_stats); anyhow!( "Failed to deserialize sub stats into mona::artifacts::StatName: {}. Serialized data: \n{}", diff --git a/src/applications/input/buff.rs b/src/applications/input/buff.rs index 0fb0c77..d20b5ab 100644 --- a/src/applications/input/buff.rs +++ b/src/applications/input/buff.rs @@ -13,22 +13,22 @@ use mona_wasm::applications::common::BuffInterface as MonaBuffInterface; #[derive(Clone)] pub struct PyBuffInterface { #[pyo3(get, set)] - pub name: Py, + pub name: Bound<'_, PyString>, #[pyo3(get, set)] - pub config: Option>, + pub config: Option>, } #[pymethods] impl PyBuffInterface { #[new] - pub fn py_new(name: Py, config: Option>) -> PyResult { + pub fn py_new(name: Bound<'_, PyString>, 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.to_str()?; let config_repr = match &self.config { - Some(config) => config.as_ref(py).repr()?.to_str()?.to_string(), + Some(config) => config.as_ref().repr()?.to_str()?.to_string(), None => "None".to_string(), }; Ok(format!( @@ -40,10 +40,10 @@ impl PyBuffInterface { #[getter] pub fn __dict__(&self, py: Python) -> PyResult { let dict = PyDict::new(py); - let name_str = self.name.as_ref(py).to_str()?; + let name_str = self.name.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)?; } else { dict.set_item("config", py.None())?; } @@ -56,9 +56,8 @@ impl TryInto for PyBuffInterface { fn try_into(self) -> Result { let name: BuffName = Python::with_gil(|py| { - let _string: &PyString = self.name.as_ref(py); - depythonize(_string).map_err(|err| { - let serialized_data = format!("{:?}", _string); + depythonize(&*self.name).map_err(|err| { + let serialized_data = format!("{:?}", self.name); anyhow!( "Failed to deserialize name into mona::buffs::buff_name::BuffName: {}. Serialized data: \n{}", err, @@ -69,9 +68,8 @@ impl TryInto for PyBuffInterface { let config: BuffConfig = if let Some(value) = self.config { Python::with_gil(|py| { - let _dict: &PyDict = value.as_ref(py); - depythonize(_dict).map_err(|err| { - let serialized_data = format!("{:?}", _dict); + depythonize(&*value).map_err(|err| { + let serialized_data = format!("{:?}", value); anyhow!( "Failed to deserialize config into mona::buffs::BuffConfig: {}. Serialized data: \n{}", err, diff --git a/src/applications/input/calculator.rs b/src/applications/input/calculator.rs index dcf25d9..61be4bf 100644 --- a/src/applications/input/calculator.rs +++ b/src/applications/input/calculator.rs @@ -20,7 +20,7 @@ pub struct PyCalculatorConfig { #[pyo3(get, set)] pub artifacts: Vec, #[pyo3(get, set)] - pub artifact_config: Option>, + pub artifact_config: Option>, #[pyo3(get, set)] pub skill: PySkillInterface, #[pyo3(get, set)] @@ -37,7 +37,7 @@ impl PyCalculatorConfig { skill: PySkillInterface, buffs: Option>, artifacts: Option>, - artifact_config: Option>, + artifact_config: Option>, enemy: Option, ) -> PyResult { Ok(Self { @@ -61,13 +61,13 @@ impl PyCalculatorConfig { .iter() .map(|b| b.__dict__(py)) .collect::, PyErr>>()?; - dict.set_item("buffs", PyList::new(py, buffs))?; + 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))?; + dict.set_item("artifacts", PyList::new(py, artifacts)?)?; if let Some(artifact_config) = self.artifact_config.as_ref().map(|c| c.as_ref(py)) { dict.set_item("artifact_config", artifact_config)?; } else { diff --git a/src/applications/input/character.rs b/src/applications/input/character.rs index b136d76..3c6eb84 100644 --- a/src/applications/input/character.rs +++ b/src/applications/input/character.rs @@ -26,7 +26,7 @@ pub struct PyCharacterInterface { #[pyo3(get, set)] pub skill3: usize, #[pyo3(get, set)] - pub params: Option>, + pub params: Option>, } #[pymethods] @@ -40,7 +40,7 @@ impl PyCharacterInterface { skill1: usize, skill2: usize, skill3: usize, - params: Option>, + params: Option>, ) -> PyResult { Ok(Self { name, @@ -95,9 +95,8 @@ 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); - depythonize(_dict).map_err(|err| { - let serialized_data = format!("{:?}", _dict); + depythonize(&value).map_err(|err| { + let serialized_data = format!("{:?}", value); anyhow!( "Failed to deserialize params into mona::character::CharacterConfig: {}. Serialized data: \n{}", err, @@ -145,7 +144,7 @@ mod tests { skill1: 12, skill2: 12, skill3: 12, - params: Some(Py::from(outer_dict)), + params: Option::from(outer_dict), }; assert_eq!(py_character_interface.name, "HuTao"); @@ -158,11 +157,10 @@ mod tests { match &py_character_interface.params { Some(value) => { - let py_dict = value.as_ref(py); + let py_dict = value.as_ref(); let hutao_dict = py_dict .get_item("HuTao") .unwrap() - .unwrap() .downcast::() .unwrap(); assert_eq!( diff --git a/src/applications/input/skill.rs b/src/applications/input/skill.rs index d7f9e3b..4920607 100644 --- a/src/applications/input/skill.rs +++ b/src/applications/input/skill.rs @@ -12,13 +12,13 @@ pub struct PySkillInterface { #[pyo3(get, set)] pub index: usize, #[pyo3(get, set)] - pub config: Option>, + pub config: Option>, } #[pymethods] impl PySkillInterface { #[new] - fn new(index: usize, config: Option>) -> PyResult { + fn new(index: usize, config: Option>) -> PyResult { Ok(Self { index, config }) } pub fn __repr__(&self) -> PyResult { @@ -33,7 +33,7 @@ impl PySkillInterface { 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)?; } else { dict.set_item("config", py.None())?; } @@ -47,9 +47,8 @@ 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); - depythonize(_dict).map_err(|err| { - let serialized_data = format!("{:?}", _dict); + depythonize(&*value).map_err(|err| { + let serialized_data = format!("{:?}", value); 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..f2d9292 100644 --- a/src/applications/input/weapon.rs +++ b/src/applications/input/weapon.rs @@ -11,7 +11,7 @@ use mona_wasm::applications::common::WeaponInterface as MonaWeaponInterface; #[derive(Clone)] pub struct PyWeaponInterface { #[pyo3(get, set)] - pub name: Py, + pub name: Bound<'_, PyString>, #[pyo3(get, set)] pub level: i32, #[pyo3(get, set)] @@ -19,7 +19,7 @@ pub struct PyWeaponInterface { #[pyo3(get, set)] pub refine: i32, #[pyo3(get, set)] - pub params: Option>, + pub params: Bound<'_, PyDict>, } #[pymethods] @@ -41,10 +41,10 @@ impl PyWeaponInterface { }) } - pub fn __repr__(&self, py: Python) -> PyResult { - let name = self.name.as_ref(py).to_str()?; + pub fn __repr__(&self) -> PyResult { + let name = self.name.as_ref().to_str()?; let params_repr = match &self.params { - Some(params) => params.as_ref(py).repr()?.to_str()?.to_string(), + Some(params) => params.as_ref().repr()?.to_str()?.to_string(), None => "None".to_string(), }; @@ -57,12 +57,12 @@ impl PyWeaponInterface { #[getter] pub fn __dict__(&self, py: Python) -> PyResult { let dict = PyDict::new(py); - dict.set_item("name", self.name.as_ref(py))?; + dict.set_item("name", self.name.as_ref())?; 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.as_ref())?; } else { dict.set_item("params", py.None())?; } @@ -75,9 +75,8 @@ impl TryInto for PyWeaponInterface { fn try_into(self) -> Result { let name: WeaponName = Python::with_gil(|py| { - let _string: &PyString = self.name.as_ref(py); - depythonize(_string).map_err(|err| { - let serialized_data = format!("{:?}", _string); + depythonize(&self.name).map_err(|err| { + let serialized_data = format!("{:?}", self.name); anyhow!( "Failed to deserialize name into mona::weapon::WeaponName: {}. Serialized data: \n{}", err, @@ -132,15 +131,15 @@ mod tests { let name = PyString::new(py, "StaffOfHoma"); let py_weapon_interface = PyWeaponInterface { - name: Py::from(name), + name: name, level: 90, ascend: true, refine: 5, - params: Some(Py::from(params_dict)), + params: params_dict, }; assert_eq!( - py_weapon_interface.name.as_ref(py).to_string(), + py_weapon_interface.name.as_ref().to_string(), "StaffOfHoma" ); assert_eq!(py_weapon_interface.level, 90); @@ -149,7 +148,7 @@ mod tests { match &py_weapon_interface.params { Some(value) => { - let py_dict = value.as_ref(py); + let py_dict = value.as_ref(); let params_dict = py_dict .get_item("StaffOfHoma") .unwrap() diff --git a/src/applications/output/damage_analysis.rs b/src/applications/output/damage_analysis.rs index 7cf1678..5db3eea 100644 --- a/src/applications/output/damage_analysis.rs +++ b/src/applications/output/damage_analysis.rs @@ -77,7 +77,7 @@ impl PyDamageAnalysis { let dict = PyDict::new(py); fn insert_hashmap( - dict: &PyDict, + dict: &Bound, py: Python, key: &str, hashmap: &HashMap, @@ -90,27 +90,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)?; diff --git a/src/applications/output/damage_result.rs b/src/applications/output/damage_result.rs index 049cdf7..9f6d824 100644 --- a/src/applications/output/damage_result.rs +++ b/src/applications/output/damage_result.rs @@ -57,12 +57,12 @@ impl PyDamageResult { Ok(dict.into()) } - 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())) } From e40d261b0664e64458d58a5bf23e0c12222c2efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kotori=E3=81=AE=E3=81=AD=E3=81=93?= Date: Sun, 8 Dec 2024 22:31:50 -0600 Subject: [PATCH 2/8] :bug: Update from `.as_ref(py)` to `.bind(py)` --- Cargo.toml | 2 +- src/applications/analysis.rs | 4 +- src/applications/input/artifact.rs | 59 +++++++++++----------- src/applications/input/buff.rs | 25 +++++---- src/applications/input/calculator.rs | 6 +-- src/applications/input/character.rs | 24 ++++----- src/applications/input/skill.rs | 12 +++-- src/applications/input/weapon.rs | 44 ++++++++-------- src/applications/output/damage_analysis.rs | 3 +- src/applications/output/damage_result.rs | 6 +-- src/lib.rs | 2 +- 11 files changed, 95 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d50425..c2bc6fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ name = "_python_genshin_artifact" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.23", features = ["anyhow"] } +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" } 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 5db5daa..efd25b2 100644 --- a/src/applications/input/artifact.rs +++ b/src/applications/input/artifact.rs @@ -11,17 +11,17 @@ use mona::common::StatName; #[derive(Clone)] pub struct PyArtifact { #[pyo3(get, set)] - pub set_name: String, + pub set_name: Py, #[pyo3(get, set)] - pub slot: String, + pub slot: Py, #[pyo3(get, set)] pub level: i32, #[pyo3(get, set)] pub star: i32, #[pyo3(get, set)] - pub sub_stats: Vec<(String, f64)>, + pub sub_stats: Vec<(Py, f64)>, #[pyo3(get, set)] - pub main_stat: (String, f64), + pub main_stat: (Py, f64), #[pyo3(get, set)] pub id: u64, } @@ -30,12 +30,12 @@ pub struct PyArtifact { impl PyArtifact { #[new] pub fn py_new( - set_name: String, - slot: String, + set_name: Py, + slot: Py, level: i32, star: i32, - sub_stats: Vec<(String, f64)>, - main_stat: (String, f64), + sub_stats: Vec<(Py, f64)>, + main_stat: (Py, f64), id: u64, ) -> PyResult { Ok(Self { @@ -50,9 +50,9 @@ impl PyArtifact { } pub fn __repr__(&self, py: Python) -> PyResult { - let set_name = self.set_name.as_str(); - let slot = self.slot.as_str()?; - let main_stat = self.main_stat.0.as_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={})", @@ -63,19 +63,19 @@ impl PyArtifact { #[getter] pub fn __dict__(&self, py: Python) -> PyResult { let dict = PyDict::new(py); - dict.set_item("set_name", self.set_name.as_ref())?; - dict.set_item("slot", self.slot.as_ref())?; + 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.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(), self.main_stat.1); + )?; + dict.set_item("sub_stats", sub_stats_pylist)?; + 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)?; @@ -88,9 +88,9 @@ impl TryInto for PyArtifact { fn try_into(self) -> Result { let name: ArtifactSetName = Python::with_gil(|py| { - let _string = PyString::new(py, &self.set_name); - depythonize(&_string).map_err(|err| { - let serialized_data = format!("{:?}", self.set_name); + let _string: &Bound<'_, PyString> = self.set_name.bind(py); + depythonize(_string).map_err(|err| { + let serialized_data = format!("{:?}", _string); anyhow!( "Failed to deserialize name into mona::artifacts::ArtifactSetName: {}. Serialized data: \n{}", err, @@ -100,9 +100,9 @@ impl TryInto for PyArtifact { })?; let slot: ArtifactSlotName = Python::with_gil(|py| { - let _string = PyString::new(py, &self.slot); - depythonize(&_string).map_err(|err| { - let serialized_data = format!("{:?}", &_string); + let _string: &Bound<'_, PyString> = self.slot.bind(py); + depythonize(_string).map_err(|err| { + let serialized_data = format!("{:?}", _string); anyhow!( "Failed to deserialize slot name into mona::artifacts::ArtifactSlotName: {}. Serialized data: \n{}", err, @@ -112,9 +112,9 @@ impl TryInto for PyArtifact { })?; let main_stat_name: StatName = Python::with_gil(|py| { - let _string = PyString::new(py, &self.main_stat.0); - depythonize(&_string).map_err(|err| { - let serialized_data = format!("{:?}", self.main_stat.0); + 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{}", err, @@ -127,8 +127,8 @@ impl TryInto for PyArtifact { self.sub_stats .iter() .map(|s| { - let sub_stats = s.0.as_ref(); - let name: Result = depythonize(s.0.as_ref()).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 d20b5ab..50ebe5e 100644 --- a/src/applications/input/buff.rs +++ b/src/applications/input/buff.rs @@ -13,22 +13,23 @@ use mona_wasm::applications::common::BuffInterface as MonaBuffInterface; #[derive(Clone)] pub struct PyBuffInterface { #[pyo3(get, set)] - pub name: Bound<'_, PyString>, + pub name: Py, #[pyo3(get, set)] - pub config: Option>, + pub config: Option>, } #[pymethods] impl PyBuffInterface { #[new] - pub fn py_new(name: Bound<'_, PyString>, config: Option>) -> PyResult { + #[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.to_str()?; + let name = self.name.bind(py).to_str()?; let config_repr = match &self.config { - Some(config) => config.as_ref().repr()?.to_str()?.to_string(), + Some(config) => config.bind(py).repr()?.to_str()?.to_string(), None => "None".to_string(), }; Ok(format!( @@ -40,10 +41,10 @@ impl PyBuffInterface { #[getter] pub fn __dict__(&self, py: Python) -> PyResult { let dict = PyDict::new(py); - let name_str = self.name.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)?; + dict.set_item("config", config.bind(py))?; } else { dict.set_item("config", py.None())?; } @@ -56,8 +57,9 @@ impl TryInto for PyBuffInterface { fn try_into(self) -> Result { let name: BuffName = Python::with_gil(|py| { - depythonize(&*self.name).map_err(|err| { - let serialized_data = format!("{:?}", self.name); + let _string: &Bound<'_, PyString> = self.name.bind(py); + depythonize(_string).map_err(|err| { + let serialized_data = format!("{:?}", _string); anyhow!( "Failed to deserialize name into mona::buffs::buff_name::BuffName: {}. Serialized data: \n{}", err, @@ -68,8 +70,9 @@ impl TryInto for PyBuffInterface { let config: BuffConfig = if let Some(value) = self.config { Python::with_gil(|py| { - depythonize(&*value).map_err(|err| { - let serialized_data = format!("{:?}", value); + let _dict: &Bound<'_, PyDict> = value.bind(py); + depythonize(_dict).map_err(|err| { + let serialized_data = format!("{:?}", _dict); anyhow!( "Failed to deserialize config into mona::buffs::BuffConfig: {}. Serialized data: \n{}", err, diff --git a/src/applications/input/calculator.rs b/src/applications/input/calculator.rs index 61be4bf..03ff512 100644 --- a/src/applications/input/calculator.rs +++ b/src/applications/input/calculator.rs @@ -20,7 +20,7 @@ pub struct PyCalculatorConfig { #[pyo3(get, set)] pub artifacts: Vec, #[pyo3(get, set)] - pub artifact_config: Option>, + pub artifact_config: Option>, #[pyo3(get, set)] pub skill: PySkillInterface, #[pyo3(get, set)] @@ -37,7 +37,7 @@ impl PyCalculatorConfig { skill: PySkillInterface, buffs: Option>, artifacts: Option>, - artifact_config: Option>, + artifact_config: Option>, enemy: Option, ) -> PyResult { Ok(Self { @@ -68,7 +68,7 @@ impl PyCalculatorConfig { .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)) { + 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())?; diff --git a/src/applications/input/character.rs b/src/applications/input/character.rs index 3c6eb84..1f37f91 100644 --- a/src/applications/input/character.rs +++ b/src/applications/input/character.rs @@ -26,12 +26,13 @@ pub struct PyCharacterInterface { #[pyo3(get, set)] pub skill3: usize, #[pyo3(get, set)] - pub params: Option>, + pub params: Option>, } #[pymethods] impl PyCharacterInterface { #[new] + #[pyo3(signature = (name, level, ascend, constellation, skill1, skill2, skill3, params=None))] pub fn py_new( name: String, level: usize, @@ -40,7 +41,7 @@ impl PyCharacterInterface { skill1: usize, skill2: usize, skill3: usize, - params: Option>, + params: Option>, ) -> PyResult { Ok(Self { name, @@ -95,8 +96,9 @@ 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| { - depythonize(&value).map_err(|err| { - let serialized_data = format!("{:?}", value); + let _dict: &Bound<'_, PyDict> = value.bind(py); + depythonize(_dict).map_err(|err| { + let serialized_data = format!("{:?}", _dict); anyhow!( "Failed to deserialize params into mona::character::CharacterConfig: {}. Serialized data: \n{}", err, @@ -144,7 +146,7 @@ mod tests { skill1: 12, skill2: 12, skill3: 12, - params: Option::from(outer_dict), + params: Some(Py::from(outer_dict)), }; assert_eq!(py_character_interface.name, "HuTao"); @@ -157,12 +159,9 @@ mod tests { match &py_character_interface.params { Some(value) => { - let py_dict = value.as_ref(); - let hutao_dict = py_dict - .get_item("HuTao") - .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") @@ -209,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/skill.rs b/src/applications/input/skill.rs index 4920607..7e9785a 100644 --- a/src/applications/input/skill.rs +++ b/src/applications/input/skill.rs @@ -12,13 +12,14 @@ pub struct PySkillInterface { #[pyo3(get, set)] pub index: usize, #[pyo3(get, set)] - pub config: Option>, + pub config: Option>, } #[pymethods] impl PySkillInterface { #[new] - fn new(index: usize, config: Option>) -> PyResult { + #[pyo3(signature = (index, config=None))] + fn new(index: usize, config: Option>) -> PyResult { Ok(Self { index, config }) } pub fn __repr__(&self) -> PyResult { @@ -33,7 +34,7 @@ impl PySkillInterface { let dict = PyDict::new(py); dict.set_item("index", self.index)?; if let Some(config) = &self.config { - dict.set_item("config", config)?; + dict.set_item("config", config.bind(py))?; } else { dict.set_item("config", py.None())?; } @@ -47,8 +48,9 @@ impl TryInto for PySkillInterface { fn try_into(self) -> Result { let config: CharacterSkillConfig = if let Some(value) = self.config { Python::with_gil(|py| { - depythonize(&*value).map_err(|err| { - let serialized_data = format!("{:?}", value); + 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 f2d9292..ad3a4cd 100644 --- a/src/applications/input/weapon.rs +++ b/src/applications/input/weapon.rs @@ -11,7 +11,7 @@ use mona_wasm::applications::common::WeaponInterface as MonaWeaponInterface; #[derive(Clone)] pub struct PyWeaponInterface { #[pyo3(get, set)] - pub name: Bound<'_, PyString>, + pub name: Py, #[pyo3(get, set)] pub level: i32, #[pyo3(get, set)] @@ -19,12 +19,13 @@ pub struct PyWeaponInterface { #[pyo3(get, set)] pub refine: i32, #[pyo3(get, set)] - pub params: Bound<'_, PyDict>, + pub params: Option>, } #[pymethods] impl PyWeaponInterface { #[new] + #[pyo3(signature = (name, level, ascend, refine, params=None))] pub fn py_new( name: Py, level: i32, @@ -41,10 +42,10 @@ impl PyWeaponInterface { }) } - pub fn __repr__(&self) -> PyResult { - let name = self.name.as_ref().to_str()?; + pub fn __repr__(&self, py: Python) -> PyResult { + let name = self.name.bind(py).to_str()?; let params_repr = match &self.params { - Some(params) => params.as_ref().repr()?.to_str()?.to_string(), + Some(params) => params.bind(py).repr()?.to_str()?.to_string(), None => "None".to_string(), }; @@ -57,12 +58,12 @@ impl PyWeaponInterface { #[getter] pub fn __dict__(&self, py: Python) -> PyResult { let dict = PyDict::new(py); - dict.set_item("name", self.name.as_ref())?; + 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())?; + dict.set_item("params", params.bind(py))?; } else { dict.set_item("params", py.None())?; } @@ -75,8 +76,9 @@ impl TryInto for PyWeaponInterface { fn try_into(self) -> Result { let name: WeaponName = Python::with_gil(|py| { - depythonize(&self.name).map_err(|err| { - let serialized_data = format!("{:?}", self.name); + let _string: &Bound<'_, PyString> = self.name.bind(py); + depythonize(_string).map_err(|err| { + let serialized_data = format!("{:?}", _string); anyhow!( "Failed to deserialize name into mona::weapon::WeaponName: {}. Serialized data: \n{}", err, @@ -87,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!( @@ -131,30 +133,23 @@ mod tests { let name = PyString::new(py, "StaffOfHoma"); let py_weapon_interface = PyWeaponInterface { - name: name, + name: Py::from(name), level: 90, ascend: true, refine: 5, - params: params_dict, + params: Some(Py::from(params_dict)), }; - assert_eq!( - py_weapon_interface.name.as_ref().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(); - 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") @@ -222,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 5db3eea..7bd1d43 100644 --- a/src/applications/output/damage_analysis.rs +++ b/src/applications/output/damage_analysis.rs @@ -73,7 +73,8 @@ pub struct PyDamageAnalysis { #[pymethods] impl PyDamageAnalysis { #[getter] - fn __dict__(&self, py: Python) -> PyResult { // skipcq: RS-R1000 + fn __dict__(&self, py: Python) -> PyResult { + // skipcq: RS-R1000 let dict = PyDict::new(py); fn insert_hashmap( diff --git a/src/applications/output/damage_result.rs b/src/applications/output/damage_result.rs index 9f6d824..1151b58 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)] @@ -62,7 +62,7 @@ impl PyDamageResult { Ok(()) } - pub fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { + pub fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { Ok(PyBytes::new(py, &serialize(&self).unwrap())) } 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)?)?; From 130bc77ca8c015693b74c371e4c751ef593ee521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kotori=E3=81=AE=E3=81=AD=E3=81=93?= Date: Sun, 8 Dec 2024 23:07:57 -0600 Subject: [PATCH 3/8] :bug: Update `__dict__` return type to `Bound` --- src/applications/input/artifact.rs | 4 ++-- src/applications/input/buff.rs | 4 ++-- src/applications/input/calculator.rs | 8 ++++---- src/applications/input/character.rs | 4 ++-- src/applications/input/enemy.rs | 4 ++-- src/applications/input/skill.rs | 4 ++-- src/applications/input/weapon.rs | 4 ++-- src/applications/output/damage_analysis.rs | 4 ++-- src/applications/output/damage_result.rs | 4 ++-- src/applications/output/transformative_damage.rs | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/applications/input/artifact.rs b/src/applications/input/artifact.rs index efd25b2..42cc194 100644 --- a/src/applications/input/artifact.rs +++ b/src/applications/input/artifact.rs @@ -61,7 +61,7 @@ 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.bind(py))?; dict.set_item("slot", self.slot.bind(py))?; @@ -79,7 +79,7 @@ impl PyArtifact { dict.set_item("main_stat", main_stat_tuple)?; dict.set_item("id", self.id)?; - Ok(dict.into()) + Ok(dict) } } diff --git a/src/applications/input/buff.rs b/src/applications/input/buff.rs index 50ebe5e..c18e900 100644 --- a/src/applications/input/buff.rs +++ b/src/applications/input/buff.rs @@ -39,7 +39,7 @@ 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.bind(py).to_str()?; dict.set_item("name", name_str)?; @@ -48,7 +48,7 @@ impl PyBuffInterface { } else { dict.set_item("config", py.None())?; } - Ok(dict.into()) + Ok(dict) } } diff --git a/src/applications/input/calculator.rs b/src/applications/input/calculator.rs index 03ff512..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,13 +60,13 @@ impl PyCalculatorConfig { .buffs .iter() .map(|b| b.__dict__(py)) - .collect::, PyErr>>()?; + .collect::>, PyErr>>()?; dict.set_item("buffs", PyList::new(py, buffs)?)?; let artifacts = self .artifacts .iter() .map(|ar| ar.__dict__(py)) - .collect::, PyErr>>()?; + .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)?; @@ -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 1f37f91..c2b669a 100644 --- a/src/applications/input/character.rs +++ b/src/applications/input/character.rs @@ -67,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)?; @@ -84,7 +84,7 @@ impl PyCharacterInterface { dict.set_item("params", py.None())?; } - Ok(dict.into()) + Ok(dict) } } 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 7e9785a..6b630d2 100644 --- a/src/applications/input/skill.rs +++ b/src/applications/input/skill.rs @@ -30,7 +30,7 @@ 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 { @@ -38,7 +38,7 @@ impl PySkillInterface { } else { dict.set_item("config", py.None())?; } - Ok(dict.into()) + Ok(dict) } } diff --git a/src/applications/input/weapon.rs b/src/applications/input/weapon.rs index ad3a4cd..dbde1bc 100644 --- a/src/applications/input/weapon.rs +++ b/src/applications/input/weapon.rs @@ -56,7 +56,7 @@ 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.bind(py))?; dict.set_item("level", self.level)?; @@ -67,7 +67,7 @@ impl PyWeaponInterface { } else { dict.set_item("params", py.None())?; } - Ok(dict.into()) + Ok(dict) } } diff --git a/src/applications/output/damage_analysis.rs b/src/applications/output/damage_analysis.rs index 7bd1d43..3ca390f 100644 --- a/src/applications/output/damage_analysis.rs +++ b/src/applications/output/damage_analysis.rs @@ -73,7 +73,7 @@ pub struct PyDamageAnalysis { #[pymethods] impl PyDamageAnalysis { #[getter] - fn __dict__(&self, py: Python) -> PyResult { + fn __dict__<'py>(&self, py: Python<'py>) -> PyResult> { // skipcq: RS-R1000 let dict = PyDict::new(py); @@ -144,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 1151b58..4716d86 100644 --- a/src/applications/output/damage_result.rs +++ b/src/applications/output/damage_result.rs @@ -47,14 +47,14 @@ 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: Bound<'_, PyBytes>) -> PyResult<()> { 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) } } From 185915e8817a2d628a091b28e01a77dc36e14dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kotori=E3=81=AE=E3=81=AD=E3=81=93?= Date: Sun, 8 Dec 2024 23:49:06 -0600 Subject: [PATCH 4/8] :sparkles: Add docs for PyO3 upgrade 0.20.x to 0.23.x --- docs/PyO3_v0.23.0_upgrade.md | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/PyO3_v0.23.0_upgrade.md 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) From 9e2a167c0eec6bfe4bbbedf071b95366f6b4cf37 Mon Sep 17 00:00:00 2001 From: LuoShui Date: Mon, 9 Dec 2024 14:51:50 +0800 Subject: [PATCH 5/8] :art: Add instructions to `Cargo.toml` --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c2bc6fd..fee7e26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ name = "_python_genshin_artifact" crate-type = ["cdylib"] [dependencies] +# 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" } From 79e3cce571c2d78f303334c437a593d2ed9131cd Mon Sep 17 00:00:00 2001 From: LuoShui Date: Mon, 9 Dec 2024 14:52:15 +0800 Subject: [PATCH 6/8] :art: Update `pyproject.toml` --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) 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", From 47f4e256cc767876dfa9a913433cafba3d39774c Mon Sep 17 00:00:00 2001 From: LuoShui Date: Mon, 9 Dec 2024 14:59:04 +0800 Subject: [PATCH 7/8] :art: Update CI Test --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab9cf96..1de6273 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: From f667a337effce88c11301e14f6446e18b87125c3 Mon Sep 17 00:00:00 2001 From: LuoShui Date: Mon, 9 Dec 2024 15:00:24 +0800 Subject: [PATCH 8/8] :art: Update CI build --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1de6273..090ba72 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: