デコレータがついているとdocstringが取れない？

久々にPythonの記事です。
なんかハマった（そして解決できていない）ので、書いておくことにしました。

※ 追記あり

# 事象

docstringを取得するためのテスト用に以下のスクリプトを書きました。

```python:test.py
import re


class Test:

    def __init__(self):
        pass

    @classmethod
    def hoge(cls):
        """メソッド一覧."""
        methods = [a for a in dir(cls) if '_' not in a]
        for _method in methods:
            docstring = re.sub(' {1,}', '', getattr(cls, _method).__doc__)
            args = re.split(r'\.\n', docstring)
            title = args[0]
            description = '\n'.join((''.join(args[1:])).split('\n')[1:])
            print(_method)
            print(title)
            print(description)
        return

    def fuga(self, arg1):
        """引数をprintするだけ.

        ただそれだけ。
        """
        print(arg1)

    def foo(self, arg2):
        """引数は全く関係ない.

        いらないね。
        """
        return 'foo'


def main():
    Test.hoge()


if __name__ == '__main__':
    main()
```

この`test.py`をそのまま実行すると、結果は以下になります。

```console
foo
引数は全く関係ない
いらないね。

fuga
引数をprintするだけ
ただそれだけ。

hoge
メソッド一覧.
```

docstringが取得できていますね。

そこで、ロギング用に以下のデコレータを作成しました。

```python:decos.py
def log(logger):
    """ロギングする."""

    def log_wrapper(func):
        """ロギングラッパー."""

        def wrapper(*args, **kwargs):
            """ラッパー実体."""
            logger.info(f"--START-- {func.__name__}")
            res = func(*args, **kwargs)
            logger.debug(f"res: {res}")
            logger.info(f"-- END -- {func.__name__}")
            return res

        return wrapper

    return log_wrapper
```

実行をロギングしたかっただけです。

このデコレータはロガーを受け取って使用するタイプなので、`test.py`に手を加えます。

```python:test.py
import logging
import re

from decos import log

LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
formatter = logging.Formatter(
    '%(asctime)s [%(levelname)s] %(message)s [%(filename)s in %(lineno)d]')
stream_handler.setFormatter(formatter)
LOGGER.addHandler(stream_handler)


class Test:

    def __init__(self):
        pass

    @classmethod
    def hoge(cls):
        """メソッド一覧."""
        methods = [a for a in dir(cls) if '_' not in a]
        for _method in methods:
            docstring = re.sub(' {1,}', '', getattr(cls, _method).__doc__)
            args = re.split(r'\.\n', docstring)
            title = args[0]
            description = '\n'.join((''.join(args[1:])).split('\n')[1:])
            print(_method)
            print(title)
            print(description)
        return

    @log(LOGGER)
    def fuga(self, arg1):
        """引数をprintするだけ.

        ただそれだけ。
        """
        print(arg1)

    @log(LOGGER)
    def foo(self, arg2):
        """引数は全く関係ない.

        いらないね。
        """
        return 'foo'


def main():
    Test.hoge()
    test = Test()
    test.fuga('ふがふが')


if __name__ == '__main__':
    main()
```

これで、メソッドの実行前後にログ出力するようになりました。
実行してみると・・・

```console
foo
ラッパー実体.

fuga
ラッパー実体.

hoge
メソッド一覧.

2021-05-05 10:35:44,750 [INFO] --START-- fuga [decos.py in 9]
ふがふが
2021-05-05 10:35:44,750 [INFO] -- END -- fuga [decos.py in 12]
```

こんな風に、デコレータのdocstringに置き換わってしまいます。
何故・・・

解決策が分かれば追記したいと思います。

# 追記

コメントを受けて、強引ですが解決策が分かりました。
@kts_h さん、ありがとうございます。

デコレータを以下のように修正します。

```python:decos.py
def log(logger):
    """ロギングする."""

    def log_wrapper(func):
        """ロギングラッパー."""

        def wrapper(*args, **kwargs):
            """ラッパー実体."""
            logger.info(f"--START-- {func.__name__}")
            res = func(*args, **kwargs)
            logger.debug(f"res: {res}")
            logger.info(f"-- END -- {func.__name__}")
            return res

        wrapper.__doc__ = func.__doc__

        return wrapper

    return log_wrapper
```

めっちゃ強引ですが、`__doc__`を書き換えます。
これで実行してみると、出力が以下になりました。

```console
foo
引数は全く関係ない
いらないね。

fuga
引数をprintするだけ
ただそれだけ。

hoge
メソッド一覧.

2021-05-05 12:39:47,891 [INFO] --START-- fuga [decos.py in 9]
ふがふが
2021-05-05 12:39:47,891 [INFO] -- END -- fuga [decos.py in 12]
```

見事解決！？