From 61507ca37c29dc92c275d92883725fdb27d2411b Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Mon, 27 Jan 2025 02:43:39 +0900 Subject: [PATCH] =?UTF-8?q?feat!:=20[Python]=20`Enum`=E3=82=92`Literal`?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../python/voicevox_core/_models.py | 91 ++++++++----------- .../python/voicevox_core/_rust/asyncio.pyi | 5 +- .../python/voicevox_core/_rust/blocking.pyi | 5 +- .../voicevox_core_python_api/src/convert.rs | 33 +++---- docs/guide/dev/api-design.md | 1 + example/python/run-asyncio.py | 10 +- example/python/run.py | 10 +- 7 files changed, 74 insertions(+), 81 deletions(-) diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_models.py b/crates/voicevox_core_python_api/python/voicevox_core/_models.py index 02a8f87b3..f48a16fb6 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_models.py +++ b/crates/voicevox_core_python_api/python/voicevox_core/_models.py @@ -1,6 +1,5 @@ import dataclasses -from enum import Enum -from typing import NewType +from typing import Literal, NewType, TypeAlias from uuid import UUID import pydantic @@ -34,21 +33,19 @@ x : UUID """ +StyleType: TypeAlias = Literal["talk", "singing_teacher", "frame_decode", "sing"] +""" -class StyleType(str, Enum): - """**スタイル** (_style_)に対応するモデルの種類。""" - - TALK = "talk" - """音声合成クエリの作成と音声合成が可能。""" - - SINGING_TEACHER = "singing_teacher" - """歌唱音声合成用のクエリの作成が可能。""" - - FRAME_DECODE = "frame_decode" - """歌唱音声合成が可能。""" +**スタイル** (_style_)に対応するモデルの種類。 - SING = "sing" - """歌唱音声合成用のクエリの作成と歌唱音声合成が可能。""" +===================== ================================================== +値 説明 +``"talk"`` 音声合成クエリの作成と音声合成が可能。 +``"singing_teacher"`` 歌唱音声合成用のクエリの作成が可能。 +``"frame_decode"`` 歌唱音声合成用のクエリの作成が可能。 +``"sing"`` 歌唱音声合成用のクエリの作成と歌唱音声合成が可能。 +===================== ================================================== +""" @pydantic.dataclasses.dataclass @@ -61,7 +58,7 @@ class StyleMeta: id: StyleId """スタイルID。""" - type: StyleType = dataclasses.field(default=StyleType.TALK) + type: StyleType = dataclasses.field(default="talk") """スタイルに対応するモデルの種類。""" order: int | None = None @@ -129,21 +126,17 @@ class SupportedDevices: """ -class AccelerationMode(str, Enum): - """ - ハードウェアアクセラレーションモードを設定する設定値。 - """ - - AUTO = "AUTO" - """ - 実行環境に合った適切なハードウェアアクセラレーションモードを選択する。 - """ - - CPU = "CPU" - """ハードウェアアクセラレーションモードを"CPU"に設定する。""" - - GPU = "GPU" - """ハードウェアアクセラレーションモードを"GPU"に設定する。""" +AccelerationMode: TypeAlias = Literal["AUTO", "CPU", "GPU"] +""" +ハードウェアアクセラレーションモードを設定する設定値。 + +========== ====================================================================== +値 説明 +``"AUTO"`` 実行環境に合った適切なハードウェアアクセラレーションモードを選択する。 +``"CPU"`` ハードウェアアクセラレーションモードを"CPU"に設定する。 +``"GPU"`` ハードウェアアクセラレーションモードを"GPU"に設定する。 +========== ====================================================================== +""" @pydantic.dataclasses.dataclass @@ -232,23 +225,21 @@ class AudioQuery: """ -class UserDictWordType(str, Enum): - """ユーザー辞書の単語の品詞。""" - - PROPER_NOUN = "PROPER_NOUN" - """固有名詞。""" - - COMMON_NOUN = "COMMON_NOUN" - """一般名詞。""" - - VERB = "VERB" - """動詞。""" - - ADJECTIVE = "ADJECTIVE" - """形容詞。""" - - SUFFIX = "SUFFIX" - """語尾。""" +UserDictWordType: TypeAlias = Literal[ + "PROPER_NOUN", "COMMON_NOUN", "VERB", "ADJECTIVE", "SUFFIX" +] +""" +ユーザー辞書の単語の品詞。 + +================= ========== +値 説明 +``"PROPER_NOUN"`` 固有名詞。 +``"COMMON_NOUN"`` 一般名詞。 +``"VERB"`` 動詞。 +``"ADJECTIVE"`` 形容詞。 +``"SUFFIX"`` 語尾。 +================= ========== +""" @pydantic.dataclasses.dataclass @@ -272,9 +263,7 @@ class UserDictWord: 音が下がる場所を指す。 """ - word_type: UserDictWordType = dataclasses.field( - default=UserDictWordType.COMMON_NOUN - ) + word_type: UserDictWordType = dataclasses.field(default="COMMON_NOUN") """品詞。""" priority: int = dataclasses.field(default=5) diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi b/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi index cc3cde7ff..284f7aadd 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi +++ b/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi @@ -1,5 +1,5 @@ from os import PathLike -from typing import TYPE_CHECKING, Literal, Union +from typing import TYPE_CHECKING, Union from uuid import UUID if TYPE_CHECKING: @@ -175,8 +175,7 @@ class Synthesizer: self, onnxruntime: Onnxruntime, open_jtalk: OpenJtalk, - acceleration_mode: AccelerationMode - | Literal["AUTO", "CPU", "GPU"] = AccelerationMode.AUTO, + acceleration_mode: AccelerationMode = "AUTO", cpu_num_threads: int = 0, ) -> None: ... def __repr__(self) -> str: ... diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi b/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi index 68482eddc..577c46af6 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi +++ b/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi @@ -1,5 +1,5 @@ from os import PathLike -from typing import TYPE_CHECKING, Literal, Union +from typing import TYPE_CHECKING, Union from uuid import UUID if TYPE_CHECKING: @@ -175,8 +175,7 @@ class Synthesizer: self, onnxruntime: Onnxruntime, open_jtalk: OpenJtalk, - acceleration_mode: AccelerationMode - | Literal["AUTO", "CPU", "GPU"] = AccelerationMode.AUTO, + acceleration_mode: AccelerationMode = "AUTO", cpu_num_threads: int = 0, ) -> None: ... def __repr__(self) -> str: ... diff --git a/crates/voicevox_core_python_api/src/convert.rs b/crates/voicevox_core_python_api/src/convert.rs index 694d23c4a..6a1cbe8ab 100644 --- a/crates/voicevox_core_python_api/src/convert.rs +++ b/crates/voicevox_core_python_api/src/convert.rs @@ -4,13 +4,13 @@ use camino::Utf8PathBuf; use easy_ext::ext; use pyo3::{ exceptions::{PyException, PyRuntimeError, PyValueError}, - types::{IntoPyDict as _, PyList}, + types::{IntoPyDict as _, PyList, PyString}, FromPyObject as _, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject, }; use serde::{de::DeserializeOwned, Serialize}; use serde_json::json; use uuid::Uuid; -use voicevox_core::{AccelerationMode, AccentPhrase, StyleId, UserDictWordType, VoiceModelMeta}; +use voicevox_core::{AccelerationMode, AccentPhrase, StyleId, VoiceModelMeta}; use crate::{ AnalyzeTextError, GetSupportedDevicesError, GpuSupportError, InitInferenceRuntimeError, @@ -21,19 +21,14 @@ use crate::{ }; pub(crate) fn from_acceleration_mode(ob: &PyAny) -> PyResult { - let py = ob.py(); - - let class = py.import("voicevox_core")?.getattr("AccelerationMode")?; - let mode = class.get_item(ob)?; - - if mode.eq(class.getattr("AUTO")?)? { - Ok(AccelerationMode::Auto) - } else if mode.eq(class.getattr("CPU")?)? { - Ok(AccelerationMode::Cpu) - } else if mode.eq(class.getattr("GPU")?)? { - Ok(AccelerationMode::Gpu) - } else { - unreachable!("{} should be one of {{AUTO, CPU, GPU}}", mode.repr()?); + match ob.extract::<&str>()? { + "AUTO" => Ok(AccelerationMode::Auto), + "CPU" => Ok(AccelerationMode::Cpu), + "GPU" => Ok(AccelerationMode::Gpu), + mode => Err(PyValueError::new_err(format!( + "`AccelerationMode` should be one of {{AUTO, CPU, GPU}}: {mode}", + mode = PyString::new(ob.py(), mode).repr()?, + ))), } } @@ -153,7 +148,7 @@ pub(crate) fn to_rust_user_dict_word(ob: &PyAny) -> PyResult( .downcast()?; to_pydantic_dataclass(word, class) } -pub(crate) fn to_rust_word_type(word_type: &PyAny) -> PyResult { - let name = word_type.getattr("name")?.extract::()?; - - serde_json::from_value::(json!(name)).into_py_value_result() +fn from_literal_choice(s: &str) -> PyResult { + serde_json::from_value::(json!(s)).into_py_value_result() } /// おおよそ以下のコードにおける`f(x)`のようなものを得る。 diff --git a/docs/guide/dev/api-design.md b/docs/guide/dev/api-design.md index c3da8b588..25d77e036 100644 --- a/docs/guide/dev/api-design.md +++ b/docs/guide/dev/api-design.md @@ -11,6 +11,7 @@ VOICEVOX CORE の主要機能は Rust で実装されることを前提として * [`StyleId`](https://voicevox.github.io/voicevox_core/apis/rust_api/voicevox_core/struct.StyleId.html)といった[newtype](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html)は、そのままnewtypeとして表現するべきです。 * 例えばPythonなら[`typing.NewType`](https://docs.python.org/ja/3/library/typing.html#newtype)で表現します。 * オプショナルな引数は、キーワード引数がある言語であればキーワード引数で、ビルダースタイルが一般的な言語であればビルダースタイルで表現すべきです。 +* 列挙型は、PythonやTypeScriptでは文字列リテラルの合併型で表現するべきです。 * [`VoiceModelFile`](https://voicevox.github.io/voicevox_core/apis/rust_api/voicevox_core/nonblocking/struct.VoiceModelFile.html)の"close"後でも`id`と`metas`は利用可能であるべきです。ただしRustにおける"close"だけは、`VoiceModelFile`を`id`と`metas`に分解するような形にします。 * `Synthesizer::render`は`range: std::ops::Range`を引数に取っています。`Range`にあたる型が標準で存在し、かつそれが配列の範囲指定として用いられるようなものであれば、それを使うべきです。 * ただし例えばPythonでは、`slice`を引数に取るのは慣習にそぐわないため`start: int, stop: int`のようにすべきです。 diff --git a/example/python/run-asyncio.py b/example/python/run-asyncio.py index 5d83593c2..a202cb776 100644 --- a/example/python/run-asyncio.py +++ b/example/python/run-asyncio.py @@ -24,12 +24,18 @@ class Args: @staticmethod def parse_args() -> "Args": + ACCELERATION_MODE_CHOICES = ("AUTO", "CPU", "GPU") + + def _(s: str): + if s in ACCELERATION_MODE_CHOICES: + _: AccelerationMode = s + argparser = ArgumentParser() argparser.add_argument( "--mode", default="AUTO", - type=AccelerationMode, - help='モード ("AUTO", "CPU", "GPU")', + choices=ACCELERATION_MODE_CHOICES, + help='モード', ) argparser.add_argument( "vvm", diff --git a/example/python/run.py b/example/python/run.py index 87b0ea85d..b99713977 100644 --- a/example/python/run.py +++ b/example/python/run.py @@ -22,12 +22,18 @@ class Args: @staticmethod def parse_args() -> "Args": + ACCELERATION_MODE_CHOICES = ("AUTO", "CPU", "GPU") + + def _(s: str): + if s in ACCELERATION_MODE_CHOICES: + _: AccelerationMode = s + argparser = ArgumentParser() argparser.add_argument( "--mode", default="AUTO", - type=AccelerationMode, - help='モード ("AUTO", "CPU", "GPU")', + choices=ACCELERATION_MODE_CHOICES, + help='モード', ) argparser.add_argument( "vvm",