はじめに
ある関数やメソッドを非推奨にして今後リプレイスしていくということは開発時によくあることだと思います。
しかし、特徴的な使われ方や名前であるならまだしも、使われ方も名前も似ている関数やメソッドがそのようなリファクタリングの対象だと非常に視認性が悪く作業効率が低下しうっかりバグを埋め込んでしまうかもしれません。
# 下記のようなディレクトリ構成と仮定
# .
# ├── __init__.py
# ├── demo.py <- このファイル
# ├── lib_a.py
# └── lib_b.py
# demo.py
from . import lib_a, lib_b
def main():
lib_a.foo()
lib_b.foo()
if __name__ == "__main__":
main()
このようなコードで「lib_a.foo
は非推奨でリプレイスするけど、lib_b.foo
はそうではないので気を付けて」と言われたら、かなり慎重な作業をしなければならず精神的に疲弊してしまうでしょう。
これを解決するのがPEP702で導入されPython3.13より標準ライブラリ入りする予定のtyping_extensions.deprecated
です。
ランタイムの動作について
typing_extensions.deprecated
はデコレータとして使われ、実行時に呼び出されると引数に渡した文字列のメッセージを持つDeprecationWaring
を表示します。
# lib_a.py
from typing_extensions import deprecated
@deprecated("削除予定")
def foo():
...
>>> python -m samples.demo
...\samples\demo.py:10: DeprecationWarning: 削除予定
lib_a.foo()
これだけであればよくある実行時に非推奨であることを表示する仕組みです。
typing_extensions.deprecated
の真価は、VSCodeのPylance
のような、IDEと型チェッカーが組み合わさった開発環境で発揮されます。
IDE表示の変化について
VSCode・Pylance
によって、typing_extensions.deprecated
でデコレートされた関数には打ち消し線が引かれます。
かつ、マウスオーバーするとツールチップには引数に取ったメッセージを表示してくれます。
これは非常に視認性がよく、単体テストが網羅していない場合でも型チェッカーが静的解析で探してくれるため非常に便利です。
一部の呼び方だけ非推奨にする
デフォルト値付きの引数を持つ関数が引数名なしで呼び出されている場合、一見してパラメータの意味が分からないときがあります。
def main():
lib_b.bar() # 引数なしで呼び出せるのか。
lib_b.bar(30) # 30ってなに?
lib_b.bar(timeout=20) # やっと意味が分かった!
これの視認性をよくするために、今後のコードでは引数名を指定することを推奨したいです。
しかし、実装をdef lib_b.bar(*, timeout=60)
としてしまうと引数なしで呼び出されている既存コードで実行時エラーとなってしまいます。
そんなとき、typing.overload
とtyping_extensions.deprecated
を組み合わせると実行時エラーを起こすことなく同じ関数の特定の呼び出し方をしている部分のみ、打ち消し線をIDEで表示させることができます。
from typing_extensions import overload
from typing_extensions import deprecated
@overload
def bar(): ...
@overload
@deprecated("タイムアウトは名前付きで指定すること")
def bar(timeout, /): ...
@overload
def bar(*, timeout=...): ...
def bar(timeout=60):
...
この使用法の注意点としては、overload
とdeprecated
でデコレートした呼び出し方で呼び出しても、実行時にDeprecationWarning
は表示されないことです。
関数の呼び出し方による警告を実行時にも表示するには、そのための分岐を別途実装に追加していく必要があります。
まとめ
VSCode・Pylance
・typing_extensions.deprecated
の組み合わせは、非推奨となった関数やメソッドを視認性よく表示させる強力な機能です。
Python3.13以降は標準ライブラリ内部にwarnings.deprecated
として追加されるので、特別な用意をすることなくリファクタリングの役に立つこの機能を使えるようになります。
これによって、実行時の動作への影響をなるべく抑えたリファクタリングがはかどっていくと考えています。