mirror of
https://github.com/PaiGramTeam/python-genshin-artifact.git
synced 2024-12-11 21:37:34 +00:00
✨ update to PyO3 0.22, add 3.13 support
Co-authored-by: kotoriのねこ <minamiktr@outlook.com>
This commit is contained in:
parent
d8117b36b2
commit
68bef8ce3d
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -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:
|
||||
|
@ -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]
|
||||
|
69
docs/PyO3_v0.23.0_upgrade.md
Normal file
69
docs/PyO3_v0.23.0_upgrade.md
Normal 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)
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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!(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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!(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)?)?;
|
||||
|
Loading…
Reference in New Issue
Block a user