Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pythonライブラリはpydanticの依存を外すか、超広範囲なバージョンを受け入れられるように変更したほうが良いかも #960

Open
3 tasks
Hiroshiba opened this issue Jan 31, 2025 · 10 comments
Labels
機能向上 要議論 実行する前に議論が必要そうなもの

Comments

@Hiroshiba
Copy link
Member

Hiroshiba commented Jan 31, 2025

内容

Pythonはデフォルトのパッケージ管理ライブラリが、パッケージが依存するパッケージのバージョンを複数持つことができません。
なのでPythonライブラリを提供する場合は、依存ライブラリに気を使い、混在できるライブラリをできる限り広くするのが城跡です。

Python版voicevox_coreはpydanticに依存していますが、受け入れられるバージョンがかなり限定的になっています。
(今のmainブランチだと>=2.5.2,<3で、2.5.2は今から1年3ヶ月前の2023年11月22日
(同じく依存しているPyhton3.10は、3.10.0のリリース日が2021年10月4日

たしか少なくとも2年だか3年だかの範囲は対応したい、みたいな話なので、受け入れられるpydanticのバージョンをもっと広くしないとな気がしました。
いっそのこと失くすのも手だと思いますが、バリデーション・シリアライズの自作をどこまでやるか次第だと思います。

Pros 良くなる点

Cons 悪くなる点

実現方法

VOICEVOXのバージョン

0.?.0

OSの種類/ディストリ/バージョン

  • Windows
  • macOS
  • Linux

その他

こちらのコメントをいただいて課題に気づきました、ありがとうございます!

個人的には、Pydanticがやってる処理のうちシリアライズはRust側からAPIを提供するから考えなくて良くて、バリデーションだけならかなり広範囲のバージョンを使えるから依存しても良い気がしています。

@Hiroshiba Hiroshiba added 機能向上 要議論 実行する前に議論が必要そうなもの labels Jan 31, 2025
@Hiroshiba
Copy link
Member Author

Hiroshiba commented Jan 31, 2025

個人的には、0.16時点ではpydanticのバージョンがかなり狭くても良いかなと思ってます。
もちろんできればもっと広くしたいけど、プルリク5つほどかかるくらいなら先に0.16出したほうが良いはず。

エンジンのjsonに合わせるためにpydanticのシリアライズをうまいこと使う部分がpydanticのバージョンを狭めているのであれば、0.16時点でパージしちゃうのが良いのではと考えています。(せっかく実装いただいたので心苦しいですが。。。)
この場合、0.16時点でのPythonライブラリはシリアライズは正式には未対応、という形になるかなと。
速攻で実装して0.17でリリースしましょう!!

@qryxip
Copy link
Member

qryxip commented Feb 2, 2025

今からPydantic v1をサポートするのはちょっとメンテ上つらくなるような気がしています。少なくともCIであるGHAのmatrixにPydantic v1とPydantic v2が生えることは確定するかなと。

ところで「0.16時点でパージ」の方の案ですが、実はPydanticなんか使わず単にdef {from,to}_jsonが付いたPyO3のオブジェクトにする方が実装上圧倒的に楽なんですよね。今現在の状態だと、Pythonの世界とRustの世界の間にはPydanticとSerdeの2つが挟まっているわけなので…

pydantic.dataclassesを使っている現状は、そっちの方が「Pythonにとって自然な形」だからという理由がありました。なので以下のコードにおいて、1つめが2つめに変わることが許容できれば、Pydantic自体の依存を外せるということになります。

# `TypeAdapter(Data).dump_json`や`TypeAdapter(Data).validate_json`でJSONとの相互変換
@pydantic.dataclasses.dataclass(frozen=True)
class ImmutableData:
    foo: T
    bar: S
    baz: U
    ...

    @pydantic.model_validator(mode="after")
    @staticmethod
    def _validate(self) -> None:
        ...

@pydantic.dataclasses.dataclass(frozen=False)
class MutableData:
    x: T
    y: S
    z: U
# dataclassである必要も無い
class ImmutableData:
    def __new__() -> NoReturn:
        raise TypeError("you can instantiate `ImmutableData` only via `ImmutableData.from_json`")

    # 中でバリデートがかかる
    def from_json(s) -> "Data": ...

    def to_json(self) -> str: ...

    @property
    def foo(self) -> T: ...
    @property
    def bar(self) -> S: ...
    @property
    def baz(self) -> U: ...

class MutableData:
    x: T
    y: S
    z: U

    def __new__(x: T, y: S, z: U) -> "MutableData": ...

    def from_json(s) -> "Data": ...

    def to_json(self) -> str: ...

    @property
    def foo(self) -> T: ...
    @property
    def bar(self) -> S: ...
    @property
    def baz(self) -> U: ...

    @foo.setter
    def foo(self, foo: T) -> None: ...
    @bar.setter
    def bar(self, bar: S) -> None: ...
    @baz.setter
    def baz(self, baz: U) -> None: ...

CC: @takaaki-inada
(何かあるのであればご意見を頂けると非常に幸いです)

@Hiroshiba
Copy link
Member Author

@qryxip 直感良さそうな気がしました!!

いったん自分がRust実装を意識せず書くなら、Immutableなものはなくして全部dictにしちゃうかもですねぇ。。
Discordで @sevenc-nanashi さんが仰ってましたが、生dataclassでも良いのかも。

@qryxip
Copy link
Member

qryxip commented Feb 2, 2025

生dataclass

バリデートという概念が上手く組み込めるかどうかですね。多分コンストラクタ部分(__new__)を塞がなきゃならないので、ちょっと調査が必要そう。ただPydanticやdataclasses-jsonが上手くやってるならそれを真似ればよさそう。

@sevenc-nanashi
Copy link
Member

__post_init__あたりにバリデーションを入れる余地がありそう?

@qryxip
Copy link
Member

qryxip commented Feb 2, 2025

ああ確かに。実際 #946 の初案(__post_init__からdataclasses側をどうにかしようとしたやつ)では上手く動作しましたね。

…じゃあ「Pydanticをやめる」をやめるというのは割とよさそうですね。ついでにserde-pyprojectも入れようかな。
(Pythonのint ←→ Rustのu32usizeとかで若干つらいことになりつつあったので)

@Hiroshiba
Copy link
Member Author

「Pydanticをやめる」をやめるというのは割とよさそう

あれ、pudanticを止める流れになってる雰囲気だと思ってました。
(pydanticのdataclassではなく、普通のdataclassを使う)

@qryxip
Copy link
Member

qryxip commented Feb 2, 2025

あ、すみません!言葉を間違えました。「Pydanticをやめて、生のdataclassにする」です。

@Hiroshiba
Copy link
Member Author

あっなるほどです!!
であれば僕も賛成寄りです!
dataclassが古くなってあまり使われなくなることはありえそうだけど、いっちょ乗ってみますか!という気持ち!

@sevenc-nanashi
Copy link
Member

sevenc-nanashi commented Feb 2, 2025

まとめ…というか、ここで問題に気がついたんですが、

Python dataclass --[dataclasses.asdict]--> Python Any --[serde-pyobject]--> Rust Model
Rust Model --[serde-pyobject]--> Python Any --[!!!]--> Python dataclass

これの!!!の部分が存在しないかもです。

追記:とは思ったけど自作できる範囲かも

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
機能向上 要議論 実行する前に議論が必要そうなもの
Projects
None yet
Development

No branches or pull requests

3 participants