Skip to content

Commit

Permalink
feat! [Python] リテラル型をnon exhaustiveに
Browse files Browse the repository at this point in the history
  • Loading branch information
qryxip committed Jan 29, 2025
1 parent 2410043 commit 71d235f
Showing 1 changed file with 138 additions and 12 deletions.
150 changes: 138 additions & 12 deletions crates/voicevox_core_python_api/python/voicevox_core/_models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import dataclasses
from typing import Literal, NewType, TypeAlias
from typing import Any, Literal, NewType, NoReturn, TypeAlias
from uuid import UUID

import pydantic
from pydantic import GetCoreSchemaHandler
from pydantic_core import CoreSchema, core_schema

from ._rust import _to_zenkaku, _validate_pronunciation


class _Reserved(str):
def __new__(cls) -> NoReturn:
raise TypeError()

@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
_ = source_type, handler
# TODO: pydantic/pydantic-core#1579 がリリースに入ったら`NeverSchema`にする
return core_schema.no_info_after_validator_function(
cls._no_input_allowed, core_schema.any_schema()
)

@classmethod
def _no_input_allowed(cls, _: object) -> NoReturn:
raise ValueError(f"No input is allowed for `{cls.__name__}`")


StyleId = NewType("StyleId", int)
"""
スタイルID。
Expand Down Expand Up @@ -33,7 +55,9 @@
x : UUID
"""

StyleType: TypeAlias = Literal["talk", "singing_teacher", "frame_decode", "sing"]
StyleType: TypeAlias = (
Literal["talk", "singing_teacher", "frame_decode", "sing"] | _Reserved
)
"""
**スタイル** (_style_)に対応するモデルの種類。
Expand All @@ -44,10 +68,44 @@
``"singing_teacher"`` 歌唱音声合成用のクエリの作成が可能。
``"frame_decode"`` 歌唱音声合成が可能。
``"sing"`` 歌唱音声合成用のクエリの作成と歌唱音声合成が可能。
``_Reserved`` 将来のために予約されている値。この値が存在することは決してない。
``str`` のサブクラスであるため、 ``StyleType`` を ``str`` として
扱うことは可能。
===================== ==================================================
``_Reserved`` の存在により、次のコードはPyright/Pylanceの型検査に通らない。これは意図的なデザインである。
.. code-block::
def _(style_type: StyleType) -> int:
match style_type:
case "talk":
return 0
case "singing_teacher":
return 1
case "frame_decode":
return 2
case "sing":
return 3
.. code-block:: text
error: Function with declared return type "int" must return value on all code paths
"None" is not assignable to "int" (reportReturnType)
``str`` として扱うことは可能。
.. code-block::
def _(style_type: StyleType):
_: str = style_type # OK
"""


def _(style_type: StyleType):
_: str = style_type


@pydantic.dataclasses.dataclass
class StyleMeta:
"""**スタイル** (_style_)のメタ情報。"""
Expand Down Expand Up @@ -126,19 +184,51 @@ class SupportedDevices:
"""


AccelerationMode: TypeAlias = Literal["AUTO", "CPU", "GPU"]
AccelerationMode: TypeAlias = Literal["AUTO", "CPU", "GPU"] | _Reserved
"""
ハードウェアアクセラレーションモードを設定する設定値。
========== ======================================================================
値 説明
``"AUTO"`` 実行環境に合った適切なハードウェアアクセラレーションモードを選択する。
``"CPU"`` ハードウェアアクセラレーションモードを"CPU"に設定する。
``"GPU"`` ハードウェアアクセラレーションモードを"GPU"に設定する。
========== ======================================================================
============= =======================================================================
値 説明
``"AUTO"`` 実行環境に合った適切なハードウェアアクセラレーションモードを選択する。
``"CPU"`` ハードウェアアクセラレーションモードを"CPU"に設定する。
``"GPU"`` ハードウェアアクセラレーションモードを"GPU"に設定する。
``_Reserved`` 将来のために予約されている値。この値が存在することは決してない。
``str`` のサブクラスであるため、 ``AccelerationMode`` を ``str`` として
扱うことは可能。
============= =======================================================================
``_Reserved`` の存在により、次のコードはPyright/Pylanceの型検査に通らない。これは意図的なデザインである。
.. code-block::
def _(mode: AccelerationMode) -> int:
match mode:
case "AUTO":
return 0
case "CPU":
return 1
case "GPU":
return 2
.. code-block:: text
error: Function with declared return type "int" must return value on all code paths
"None" is not assignable to "int" (reportReturnType)
``str`` として扱うことは可能。
.. code-block::
def _(mode: AccelerationMode):
_: str = mode # OK
"""


def _(mode: AccelerationMode):
_: str = mode


@pydantic.dataclasses.dataclass
class Mora:
"""モーラ(子音+母音)ごとの情報。"""
Expand Down Expand Up @@ -225,9 +315,9 @@ class AudioQuery:
"""


UserDictWordType: TypeAlias = Literal[
"PROPER_NOUN", "COMMON_NOUN", "VERB", "ADJECTIVE", "SUFFIX"
]
UserDictWordType: TypeAlias = (
Literal["PROPER_NOUN", "COMMON_NOUN", "VERB", "ADJECTIVE", "SUFFIX"] | _Reserved
)
"""
ユーザー辞書の単語の品詞。
Expand All @@ -238,10 +328,46 @@ class AudioQuery:
``"VERB"`` 動詞。
``"ADJECTIVE"`` 形容詞。
``"SUFFIX"`` 語尾。
``_Reserved`` 将来のために予約されている値。この値が存在することは決してない。
``str`` のサブクラスであるため、 ``UserDictWordType`` を ``str`` として
扱うことは可能。
================= ==========
``_Reserved`` の存在により、次のコードはPyright/Pylanceの型検査に通らない。これは意図的なデザインである。
.. code-block::
def _(word_type: UserDictWordType) -> int:
match word_type:
case "PROPER_NOUN":
return 0
case "COMMON_NOUN":
return 1
case "VERB":
return 2
case "ADJECTIVE":
return 3
case "SUFFIX":
return 4
.. code-block:: text
error: Function with declared return type "int" must return value on all code paths
"None" is not assignable to "int" (reportReturnType)
``str`` として扱うことは可能。
.. code-block::
def _(word_type: UserDictWordType):
_: str = word_type # OK
"""


def _(word_type: UserDictWordType):
_: str = word_type


@pydantic.dataclasses.dataclass
class UserDictWord:
"""ユーザー辞書の単語。"""
Expand Down

0 comments on commit 71d235f

Please sign in to comment.