LoginSignup
16
0

More than 1 year has passed since last update.

【Python】私のissueがきっかけでPEP585が改訂されたお話【型アノテーション】

Last updated at Posted at 2022-12-01

前置き

私はPythonでCOMオブジェクトを操作するライブラリcomtypesへのコントリビュートをしています。

その中で型アノテーションを各モジュールにつけていくことを計画しています。

comtypesはPythonバージョン互換性を持ったライブラリです。
version==1.1.11時点で2.73.33.43.53.63.73.83.93.10に対応しています。

これほどまで大量のバージョンをまたいでサポートしていると、「あるバージョンにあったモジュール/クラス/関数があるバージョンからなくなる、若しくは非推奨(deprecated)になる」「逆に追加されたりできるようになる」といったことがよく起こります。
そのため、comtypesには下記のような「ブリッジ」が書かれることがあります。

if sys.version_info >= (3, 0):
    text_type = str
else:
    text_type = unicode

アノテーションも同じように、バージョンごとでやり方に違いがあります。
2.7ではfoo: int = 1def foo(bar: int):のようなランタイムコード上でのインラインアノテーションや関数アノテーションはSyntaxErrorとなるため、PEP484で提唱されたコメントによる型アノテーションをしなければいけません。
それでもコメントによるアノテーションは現在に至るまでサポートされているため、ブリッジを書くこともなく、普段プロダクトを開発する際にやっていることと勝手が違うぐらいで特に問題ではありませんでした。

一方で、あるバージョンからdeprecatedになるものが型アノテーション関係にもありました。
それがビルトインのコレクション型でもジェネリクスとして使用できるようにするPEP585でした。

PEP585って?

詳しく説明している記事はいくらでもあるので、ここではコードスニペットでざっくりした説明だけします。

# 3.8まではタプルのアノテーションはこのやり方でしか書けなかった
from typing import Tuple
foo: Tuple[str, int] = ("hoge", 0)

# 3.9から`typing`からのimportなしで書けるようになった
foo: tuple[str, int] = ("hoge", 0)

Python3.9時点のtyping公式ドキュメントにはこう書かれています。

注釈 このモジュールは、既存の標準ライブラリクラスのサブクラスかつ、 [] 内の型変数をサポートするために Generic を拡張している、いくつかの型を定義しています。 これらの型は、既存の相当するクラスが [] をサポートするように拡張されたときに Python 3.9 で廃止になりました。
余計な型は Python 3.9 で非推奨になりましたが、非推奨の警告はどれもインタープリタから通告されません。 型チェッカーがチェックするプログラムの対照が Python 3.9 もしくはそれ以降のときに、非推奨の型に目印を付けることが期待されています。
非推奨の型は、Python 3.9.0 のリリースから5年後の初めての Python バージョンで typing モジュールから削除されます。 詳しいことは PEP 585—Type Hinting Generics In Standard Collections を参照してください。

この文面を読んで、私は

  • 「いつの日か、3.9より上のバージョンからtyping.Tupleのような"ビルトインでも書けるもの"はunicode型のように"なくなって"しまうんだ」
  • 「ランタイムの分岐と同じように、アノテーション用のシンボルもブリッジをしなきゃいけなくなるんだ」
    • if sys.version_info > (3, 9):
          from builtins import tuple as Tuple
      else:
          from typing import Tuple
      
  • こういう分岐をいちいち各モジュールのボイラープレートに書いていられないから、型ヒントのシンボルをまとめたモジュールを作ってそこからimportしよう

と考えていました。

最初のissue

そんなわけで型ヒントシンボル用のモジュールを作りimportしたのですが、VSCode上のpylanceではTupleなどのシンボルがAnyと解釈されてしまっていました。
恐らくバグだと思ったので、pylanceのリポジトリで相談しました。

原因としてはキャッシュの状態かなにかによるものだったので、設定ファイル周りをいったん更新したり再起動したりで治りました。

しかし、issueに回答してくれた、typingのコントリビューターでもあるMicrosoftテックフェローerictrautさんから「好奇心からの質問」があり、やり取りをしました。

  • 原文はissueを参照してください。

ericさん:
好奇心からですが、なぜタイピングシンボルを抽象化しようとしているのですか?
型付け記号がなくなることはないでしょう。それを使っているPythonのコードがあまりにも多く、その削除はあまりにも破壊的です。
その使用を避け、ボイラープレートや互換性モジュールで抽象化する必要はありません。

私:
> 型付け記号がなくなることはないでしょう
本当に?
typing のドキュメントには取り除かれるとありました。
> 公式ドキュメントからの引用
そしてPEP585にはこうあります。
> PEPからの引用
それともこの時点でYAGNIということになるのでしょうか?

ericさん:
> 本当に?
ええ、この文脈での「非推奨」は、後方互換性の要件がないコードに使用できる新しい推奨アプローチがあることを意味します。
これらのシンボルが削除されるわけではありません。これらのシンボルをtypingから削除するのは、あまりに混乱を招くでしょう。

私:
あなたのPythonの知識に敬意を表します。
> これらのシンボルを...削除...
まさに私が誤解していたところです。
私はためらうことなく typing を使用することにします。
このissueは解決済みで結構です。
ありがとうございました。

2つめのissue

そうはいっても現在のドキュメントや関連記事を見ているうちに不安になったため、思い切ってpythonオーガナイゼーションのtypingリポジトリまで質問しに行きました。

そこでも「typingからシンボルはなくならない」ということをPython core developerたちから聞くことができました。

discuss.python.orgへのエスカレーション

実はこの問題はPython本体のdiscussionでも取り上げられていましたが、議論が停滞していました。
しかし私のissueをきっかけにして再び動き出しました。

PEPの改訂

discussionの結果、PEP585のこの文言は「Python 3.9.0のリリースから5年後にきっと取り除く(will be removed)」から「取り除くかもしれない、少なくともPython 3.9のEOL予定の2025年10月より早くはならない(may be removed, Removal will occur no sooner than)」に変わりました。

  • それでもこういう書き方だと「提示した期日の次の日にやったなら、"早くは"なってないのだから...」というカイジの利根川のようなことを想像してしまいますが杞憂と思いましょう、そんな意地悪な人たちでは絶対ないはずです

PEPリポジトリへのPRもmergeされているので、随時周辺のドキュメントもアップデートされていくのでしょう。

備考

typingからジェネリクスが削除されなくても、Python core developerたちは「特に問題がなければビルトインのジェネリクスを使ったほうがいい」と考えています。
つまり、もしPython 3.9以降でしかプロダクションコードを書かないのであれば、typing.Tupleではなくbuiltins.tupleを使うことが推奨されています。

教訓

「不安だと思ったら、とりあえず聞いてみよう、たぶん無下に扱われはしないから」

16
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
0