廃止予定の実装であることをスマートにお知らせする - debtcollerctor

廃止予定(deprecated)なクラスやメソッドをモジュールのユーザに通知するために python では warnings があります。

以下をコードを実行すると

# -*- coding: utf-8 -*-
import warnings


def deprecated_method():
    warn_msg = "`deprecated_method` is deprecated and will be removed in v0.5"
    warnings.warn(warn_msg, UserWarning)
    print "I'm old"


if __name__ == '__main__':
    deprecated_method()

以下のようにwarningsが出ます。

$ python warning_sample.py 
warning_sample.py:7: UserWarning: `deprecated_method` is deprecated and will be removed in v0.5
  warnings.warn(warn_msg, UserWarning)
I'm old

今回は、この deprecated メッセージを作りやすくしてくれるdeptcollectorの紹介です。

debtcollectorの使い方

ほぼ、Examplesを見ればいいのですが、自分でもやってみます。

class, calssmethod, method, functionのdeprecated

クラス内のメソッドが廃止予定の場合は以下のように@removals.removeのデコレータを使います。

# -*- coding: utf-8 -*-
from debtcollector import removals
import warnings

warnings.simplefilter('always')


class Car(object):
    @removals.remove
    def start(self):
        pass


if __name__ == '__main__':
    Car().start()

実行結果は以下。

$ python removals_sample.py 
removals_sample.py:15: DeprecationWarning: Using function/method 'Car.start()' is deprecated
  Car().start()

コードの中にある、warnings.simplefilter('always')はなぜ必要なのでしょう。

@removals.removeはデフォルトだと、DeprecationWarningがwarings.warnに渡されて実行されてしますので、表示されません。 warningsの警告カテゴリに詳しくのってます。

コード内に書かずに、コマンドラインでも-Wオプションを指定することにより警告を表示できます。 さきほどのソースコードから、warnings.simplefilter('always')を削除し、以下のように実行します。

$ python -Wd removals_sample.py
... 省略
  file, filename, etc = imp.find_module(subname, path)
removals_sample.py:15: DeprecationWarning: Using function/method 'Car.start()' is deprecated
  Car().start()

Car().start()のエラーに混じって他の警告も表示されました。 自分でライブラリなどを作ったら一回やってみるといいかもしれないですね。

クラスへの警告は以下のように書きます。

@removals.remove
class Pinto(object):
    pass

クラスメソッドの場合は、@classmethodデコレータの上につけます。

 class OldAndBusted(object):
    @removals.remove
    @classmethod
    def fix_things(cls):
        pass

method, property, class, keywordなどが、他の名前に変わった時

function名が変わった時はmoves.moved_functionを使います。

# -*- coding: utf-8 -*-
from debtcollector import moves
import warnings
warnings.simplefilter('always')


def new_thing():
    return "new thing"

old_thing = moves.moved_function(new_thing, 'old_thing', __name__)


if __name__ == '__main__':
    print new_thing()
    print old_thing()

実行すると、old_thing()を呼んでも新しいメソッドが呼ばれている事がわかります。

$ python moving_sample.py 
new thing
moving_sample.py:15: DeprecationWarning: Function '__main__.old_thing()' has moved to '__main__.new_thing()'
  print old_thing()
new thing

methodの場合は、デコレータでできます。

class Cat(object):
    @moves.moved_method('meow')
    def mewow(self):
        return self.meow()
    def meow(self):
        return 'kitty'

その他の例は、最初に紹介したリンクを見てください。

将来的に置き換えられるバージョンを指定する

現在のバージョンと、置き換えれるバージョンを指定するには、以下のようにversionとremoval_versionのキーワードを指定します。

# -*- coding: utf-8 -*-
from debtcollector import moves
import warnings
warnings.simplefilter('always')


def new_thing():
    return "new thing"

old_thing = moves.moved_function(new_thing, 'old_thing', __name__,
                                 version="0.5", removal_version="0.7")


if __name__ == '__main__':
    print new_thing()
    print old_thing()

実行結果は以下です。

$ python moving_version_sample.py 
new thing
moving_version_sample.py:16: DeprecationWarning: Function '__main__.old_thing()' has moved to '__main__.new_thing()' in version '0.5' and will be removed in version '0.7'
  print old_thing()
new thing

その他の方法

以下のようにしてメソッド内でメッセージを出す事もできます。

import debtcollector
debtcollector.deprecate("This is no longer supported", version="1.0")

まとめ

deptcollectorを使うと、デコレータを使うだけでスマートに廃止予定のメソッドをユーザに通知できそうです。