Pythonモジュール中のドキュメント文字列を抜き出す


今日は、Pythonのモジュール(.py)中のクラスとそのメソッドのドキュメント文字列
を抜き出す実験をしてみました。


今回の実験では、pydocモジュールを使わず、inspectモジュールを使っています。
inspectモジュールのgetmembers()関数はすごく便利なんですが、継承関係を遡って
情報を取ってきてくれるので、あるモジュール中で定義されているクラスやメソッド
の情報だけが必要で、親クラスが持っているメソッドなどはどうでもいい場合などは
「いらない情報が多すぎる。。」とか思ったりしていました。


そこで、今回の実験では、「対象のモジュールファイル中で定義されているクラスとそのメソッドのみを対象に」
ということも含めた実験にしました。


実際のコードは以下のようになりました。

# -*- coding: utf-8 -*-

import sys
import os
import inspect

#
#
def get_module_docstr(mod):
    '''
    指定されたモジュール中のクラス、メソッドについてドキュメント文字列を
    取り出す。
  親クラスで定義されているだけのメソッドのコメント文字列は収集しない。

    戻り値:
        辞書。{'cls': クラス名文字列,
               'doc': クラスのドキュメント文字列,
               'methods': クラス中に含まれるメソッド情報のリスト。}
                          →{'method': メソッド名文字列,
                            'doc': メソッドのドキュメント文字列}
    '''
    docList = []
    for name, cls in inspect.getmembers(mod, inspect.isclass):
        # cls(クラス)を含んでいるモジュールが、別モジュールだったら
        # 不要な情報と判定
        if mod != inspect.getmodule(cls):
            continue

        cDict = {'cls': name, 'doc': None, 'methods': []}
        docList.append(cDict)
        doc = inspect.getdoc(cls)
        if doc:
            cDict['doc'] = doc

        for mName, mObj in inspect.getmembers(cls, inspect.ismethod):
            # mObj(メソッド)を含んでいるモジュールが、別モジュールだったら
            # 不要な情報と判定
            if mod != inspect.getmodule(mObj):
                continue

            mDict = {'method': mName, 'doc': None}
            cDict['methods'].append(mDict)
            
            doc = inspect.getdoc(mObj)
            if doc == None:
                continue

            mDict['doc'] = doc

    return docList


#
#
if __name__ == '__main__':
    '''
    '''
    if len(sys.argv) <= 1:
        print '*** argument error ***'
    else:
        dName, fName = os.path.split(sys.argv[1])
        if dName != None:
            sys.path.append(dName)

        mod = __import__(inspect.getmodulename(sys.argv[1]))

        for clsItem in get_module_docstr(mod):
            print '*********************'
            print clsItem['cls']
            print '*********************'

            for methodItem in clsItem['methods']:
                print '--->', methodItem['method']
                print '::doc::'
                print methodItem['doc']
                print ''                


上記の

inspect.getmodule(object)

は結構便利ですね。指定したオブジェクトが含まれているモジュールを返してくれます。


と、ここまではいい感じでドキュメント文字列が取得できていたのですが、
一つ問題に気づきました。それは。。。

処理対象のモジュールを__import__()するので、エラーがあるようなファイルは
処理できない。。という点です。
たとえば、処理対象のモジュールから外部モジュールをimportしているけど、その外部モジュール
のファイルが存在しないとか。。


ううむ。。困りました。


ということで、次は「正規表現でドキュメント文字列を抜き出してみる」を試してみる予定です。

つづく。