Skip to content

Commit

Permalink
blocking.{OpenJtalk,UserDict}
Browse files Browse the repository at this point in the history
  • Loading branch information
qryxip committed Dec 5, 2023
1 parent 67c0852 commit b9edd5b
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/generate_document.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ jobs:
run: |
cargo build -p voicevox_core_c_api -vv
maturin develop --manifest-path ./crates/voicevox_core_python_api/Cargo.toml --locked
# https://github.com/readthedocs/sphinx-autoapi/issues/405
- name: Workaround to make Sphinx recognize `_rust` as a module
run: touch ./crates/voicevox_core_python_api/python/voicevox_core/_rust/__init__.py
- name: Generate Sphinx document
run: sphinx-build docs/apis/python_api public/apis/python_api
- name: Generate Javadoc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@
supported_devices,
)

from . import blocking # noqa: F401 isort: skip

__all__ = [
"__version__",
"AccelerationMode",
"AccentPhrase",
"AudioQuery",
"blocking",
"ExtractFullContextLabelError",
"GetSupportedDevicesError",
"GpuSupportError",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from pathlib import Path
from typing import TYPE_CHECKING, Dict, Union
from uuid import UUID

if TYPE_CHECKING:
from voicevox_core import UserDictWord

class OpenJtalk:
"""
テキスト解析器としてのOpen JTalk。
Parameters
----------
open_jtalk_dict_dir
Open JTalkの辞書ディレクトリ。
"""

def __init__(self, open_jtalk_dict_dir: Union[Path, str]) -> None: ...
def use_user_dict(self, user_dict: UserDict) -> None:
"""
ユーザー辞書を設定する。
この関数を呼び出した後にユーザー辞書を変更した場合は、再度この関数を呼ぶ必要がある。
Parameters
----------
user_dict
ユーザー辞書。
"""
...

class UserDict:
"""ユーザー辞書。"""

@property
def words(self) -> Dict[UUID, UserDictWord]:
"""このオプジェクトの :class:`dict` としての表現。"""
...
def __init__(self) -> None: ...
def load(self, path: str) -> None:
"""ファイルに保存されたユーザー辞書を読み込む。
Parameters
----------
path
ユーザー辞書のパス。
"""
...
def save(self, path: str) -> None:
"""
ユーザー辞書をファイルに保存する。
Parameters
----------
path
ユーザー辞書のパス。
"""
...
def add_word(self, word: UserDictWord) -> UUID:
"""
単語を追加する。
Parameters
----------
word
追加する単語。
Returns
-------
単語のUUID。
"""
...
def update_word(self, word_uuid: UUID, word: UserDictWord) -> None:
"""
単語を更新する。
Parameters
----------
word_uuid
更新する単語のUUID。
word
新しい単語のデータ。
"""
...
def remove_word(self, word_uuid: UUID) -> None:
"""
単語を削除する。
Parameters
----------
word_uuid
削除する単語のUUID。
"""
...
def import_dict(self, other: UserDict) -> None:
"""
ユーザー辞書をインポートする。
Parameters
----------
other
インポートするユーザー辞書。
"""
...
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ._rust.blocking import OpenJtalk, UserDict

__all__ = ["OpenJtalk", "UserDict"]
2 changes: 1 addition & 1 deletion crates/voicevox_core_python_api/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub fn to_rust_word_type(word_type: &PyAny) -> PyResult<UserDictWordType> {
serde_json::from_value::<UserDictWordType>(json!(name)).into_py_value_result()
}

#[ext]
#[ext(VoicevoxCoreResultExt)]
pub impl<T> voicevox_core::Result<T> {
fn into_py_result(self, py: Python<'_>) -> PyResult<T> {
use voicevox_core::ErrorKind;
Expand Down
136 changes: 128 additions & 8 deletions crates/voicevox_core_python_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use log::debug;
use pyo3::{
create_exception,
exceptions::{PyException, PyKeyError, PyValueError},
pyclass, pyfunction, pymethods, pymodule,
py_run, pyclass, pyfunction, pymethods, pymodule,
types::{IntoPyDict as _, PyBytes, PyDict, PyList, PyModule},
wrap_pyfunction, PyAny, PyObject, PyRef, PyResult, PyTypeInfo, Python, ToPyObject,
};
Expand All @@ -18,20 +18,31 @@ use voicevox_core::{

#[pymodule]
#[pyo3(name = "_rust")]
fn rust(_: Python<'_>, module: &PyModule) -> PyResult<()> {
fn rust(py: Python<'_>, module: &PyModule) -> PyResult<()> {
pyo3_log::init();

module.add("__version__", env!("CARGO_PKG_VERSION"))?;
module.add_wrapped(wrap_pyfunction!(supported_devices))?;
module.add_wrapped(wrap_pyfunction!(_validate_pronunciation))?;
module.add_wrapped(wrap_pyfunction!(_to_zenkaku))?;

module.add_class::<Synthesizer>()?;
module.add_class::<OpenJtalk>()?;
module.add_class::<VoiceModel>()?;
module.add_class::<UserDict>()?;

add_exceptions(module)
module.add_class::<self::Synthesizer>()?;
module.add_class::<self::OpenJtalk>()?;
module.add_class::<self::VoiceModel>()?;
module.add_class::<self::UserDict>()?;

add_exceptions(module)?;

let blocking_module = PyModule::new(py, "voicevox_core._rust.blocking")?;
blocking_module.add_class::<self::blocking::OpenJtalk>()?;
blocking_module.add_class::<self::blocking::UserDict>()?;
// https://github.com/PyO3/pyo3/issues/1517#issuecomment-808664021
py_run!(
py,
blocking_module,
"import sys; sys.modules['voicevox_core._rust.blocking'] = blocking_module"
);
module.add_submodule(blocking_module)
}

macro_rules! exceptions {
Expand Down Expand Up @@ -609,3 +620,112 @@ impl UserDict {
Ok(words.into_py_dict(py))
}
}

mod blocking {
use std::sync::Arc;

use pyo3::{
pyclass, pymethods,
types::{IntoPyDict as _, PyDict},
PyObject, PyResult, Python,
};
use uuid::Uuid;
use voicevox_core::UserDictWord;

use crate::convert::VoicevoxCoreResultExt as _;

#[pyclass]
#[derive(Clone)]
pub(crate) struct OpenJtalk {
open_jtalk: voicevox_core::blocking::OpenJtalk,
}

#[pymethods]
impl OpenJtalk {
#[new]
fn new(
#[pyo3(from_py_with = "super::from_utf8_path")] open_jtalk_dict_dir: String,
py: Python<'_>,
) -> PyResult<Self> {
let open_jtalk =
voicevox_core::blocking::OpenJtalk::new(open_jtalk_dict_dir).into_py_result(py)?;
Ok(Self { open_jtalk })
}

fn use_user_dict(&self, user_dict: UserDict, py: Python<'_>) -> PyResult<()> {
self.open_jtalk
.use_user_dict(&user_dict.dict)
.into_py_result(py)
}
}

#[pyclass]
#[derive(Default, Debug, Clone)]
pub(crate) struct UserDict {
dict: Arc<voicevox_core::blocking::UserDict>,
}

#[pymethods]
impl UserDict {
#[new]
fn new() -> Self {
Self::default()
}

fn load(&self, path: &str, py: Python<'_>) -> PyResult<()> {
self.dict.load(path).into_py_result(py)
}

fn save(&self, path: &str, py: Python<'_>) -> PyResult<()> {
self.dict.save(path).into_py_result(py)
}

fn add_word(
&mut self,
#[pyo3(from_py_with = "crate::convert::to_rust_user_dict_word")] word: UserDictWord,
py: Python<'_>,
) -> PyResult<PyObject> {
let uuid = self.dict.add_word(word).into_py_result(py)?;

crate::convert::to_py_uuid(py, uuid)
}

fn update_word(
&mut self,
#[pyo3(from_py_with = "crate::convert::to_rust_uuid")] word_uuid: Uuid,
#[pyo3(from_py_with = "crate::convert::to_rust_user_dict_word")] word: UserDictWord,
py: Python<'_>,
) -> PyResult<()> {
self.dict.update_word(word_uuid, word).into_py_result(py)
}

fn remove_word(
&mut self,
#[pyo3(from_py_with = "crate::convert::to_rust_uuid")] word_uuid: Uuid,
py: Python<'_>,
) -> PyResult<()> {
self.dict.remove_word(word_uuid).into_py_result(py)?;
Ok(())
}

fn import_dict(&mut self, other: &UserDict, py: Python<'_>) -> PyResult<()> {
self.dict.import(&other.dict).into_py_result(py)?;
Ok(())
}

#[getter]
fn words<'py>(&self, py: Python<'py>) -> PyResult<&'py PyDict> {
let words = self.dict.with_words(|words| {
words
.iter()
.map(|(&uuid, word)| {
let uuid = crate::convert::to_py_uuid(py, uuid)?;
let word = crate::convert::to_py_user_dict_word(py, word)?;
Ok((uuid, word))
})
.collect::<PyResult<Vec<_>>>()
})?;
Ok(words.into_py_dict(py))
}
}
}
1 change: 1 addition & 0 deletions docs/apis/python_api/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

autoapi_type = "python"
autoapi_dirs = ["../../../crates/voicevox_core_python_api/python"]
autoapi_file_patterns = ["*.pyi", "*.py"]
autoapi_ignore = ["*test*"]
autoapi_options = [
"members",
Expand Down

0 comments on commit b9edd5b

Please sign in to comment.