【Python】1つのファイル内における関数の依存関係をMermaidの書式で出力する

1739 語
9 分
【Python】1つのファイル内における関数の依存関係をMermaidの書式で出力する

はじまり#

135ml avatar
135ml
よーし、出来たぜえ・・・
リサちゃん avatar
リサちゃん
お、何か作ったんかい?
135ml avatar
135ml
ああ、昨日作ったスクリプトの関数の依存関係を示すクラス図を作りたかったんだが、その時短になるスクリプトが出来たんだぜ。

スクリプトの概要#

こういうツールを作ったら便利なんじゃないかと思って、あったらあったで便利だったという話です。

何をするのか#

ざっくり言うと、ソースの中を読み取って、どの関数がどの関数を参照しているかどうかをクラスダイアグラムとして、出力してくれます。

スクリプトを実行すると、ターミナル上にMermaidの文が出力されます。

============ depends list on Markdown: start ============
# Mermaidの中身
============ depends list on Markdown: end ============

Mermaidの中身は例えばこんな感じです。

classDiagram
getSpreadsheet <|-- getSheet
getSheet <|-- getSheet
getSheet <|-- getIssuesFromGss
isPostedIssue <|-- getIssuesFromGss
getDatesByRecords <|-- makeFoldersYetExist
getDatesByRecords <|-- makeFoldersYetExist
getDatesByRecords <|-- makeFoldersYetExist
getDatesByRecords <|-- makeFoldersYetExist
getDatesNotExist <|-- makeFoldersYetExist
getDatesNotExist <|-- makeFoldersYetExist
getDatesNotExist <|-- makeFoldersYetExist
getDatesNotExist <|-- makeFoldersYetExist
makeFolderByDates <|-- makeFoldersYetExist
makeFolderByDates <|-- makeFoldersYetExist
makeFolderByDates <|-- makeFoldersYetExist
makeFolderByDates <|-- makeFoldersYetExist
getIssuesFromGss <|-- getPartsOfIssues
getIssuesFromGss <|-- getPartsOfIssues
getIssuesFromGss <|-- getPartsOfIssues
getIssuesFromGss <|-- getPartsOfIssues
getPartsOfRecords <|-- getPartsOfIssues
getPartsOfRecords <|-- getPartsOfIssues
getPartsOfRecords <|-- getPartsOfIssues
getPartsOfRecords <|-- getPartsOfIssues
getPartsOfIssues <|-- makeFoldersIntoDrive
getPartsOfIssues <|-- makeFoldersIntoDrive
getPartsOfIssues <|-- makeFoldersIntoDrive
getPartsOfIssues <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
makeFoldersYetExist <|-- makeFoldersIntoDrive
getFolderMovingInfo <|-- getFolderMovingInfo
getFolderMovingInfo <|-- getFolderMovingInfo
moveFoldersForYoutube <|-- moveFoldersForYoutube
getDatesAlreadyPosted <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
getFolderMovingInfo <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
getFolderMovingInfo <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
getFolderMovingInfo <|-- moveFoldersAlreadyPosted
getFolderMovingInfo <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
moveFoldersForYoutube <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
moveFoldersForYoutube <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
moveFoldersForYoutube <|-- moveFoldersAlreadyPosted
moveFoldersForYoutube <|-- moveFoldersAlreadyPosted
moveFoldersAlreadyPosted <|-- moveFoldersAlreadyPosted
getPartsOfRecords <|-- manageFoldersInDrive
getPartsOfRecords <|-- manageFoldersInDrive
getPartsOfIssues <|-- manageFoldersInDrive
getPartsOfRecords <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
moveFoldersAlreadyPosted <|-- manageFoldersInDrive
class getSpreadsheet{
}
class getSheet{
}
class isPostedIssue{
}
class getIssuesFromGss{
}
class getPartsOfRecords{
}
class getDatesByRecords{
}
class getDatesNotExist{
}
class makeFolderByDates{
}
class makeFoldersYetExist{
}
class getPartsOfIssues{
}
class makeFoldersIntoDrive{
}
class getDatesAlreadyPosted{
}
class getFolderMovingInfo{
}
class moveFoldersForYoutube{
}
class moveFoldersAlreadyPosted{
}
class manageFoldersInDrive{
}

出力されたMermaidをREADME.mdに記述して、GitHubで表示するとこんな感じになります。

一旦上記のスクリプトを作ってクラス図にすることで、久しぶりにスクリプトを直すときに参照関係が一目で分かるようになったので、直すのが楽になりました。

何をやっているのか#

処理の流れは以下の流れになっています:

  1. ファイルから関数を取得する。(Pythonだと'def '、JavaScriptだと'function 'を目印にして取得する。)
  2. その関数の中から、ファイルの中の関数がないかどうかを探す。
  3. 依存関係を保持した辞書型オブジェクトを出力する。(ex. { '参照元': ['参照先A', '参照先B', ...] }
  4. 辞書型オブジェクトからMermaid表記に出力する。(ex. 参照先 <|-- 参照元

最も煩雑だったところは、2〜3の部分でした。 いつから参照元の関数を切り替えるか、関数の記述ではないところを判断する部分はどこになるかなどを少し長めに考えました。

宣言部分は関数を探さないことにしたら、コードが綺麗になりました。(予約語'continue'はすごい便利ですよね。)

スクリプトで悩ましいところ#

ひとまずの目的は満たせたので良かったのですが、このスクリプトにおいて、以下の点が悩ましい・・・

  • コメントや文字列の中にある関数名も拾ってきてしまう。
  • 自分の関数も取ってきてしまう。
  • 関数の中で複数回呼び出される場合、その回数分だけ参照関係を出力する。
  • 他のファイルとの参照関係は取れない。
  • モックやフィクスチャが入っていた場合を対応していない。

コメントや文字列の中にある関数名も拾ってきてしまう。#

処理として、hogehoge(fuga)みたいに関数内で使用されている場合は、参照先として捕捉して良いのですが、下記のように記述されていたり、ロングテキストによるコメントの中などで関数名を使用されていた場合は、その関数を無視していいかどうかが一概には言えません。例えば、目印として関数名を記述しておきたいので参照しているとみなしたいこともあるかと思います。

console.log(`hogehoge(fuga)`)

ロングテキストによるコメントによると、改行されていると前後の行のクォーテーションを読まなければならなくなるので、実装するのがかなり大変になります・・・

自分の関数も取ってきてしまう。#

これに関しても一概には言えず、ログ取得の際に自分の関数名を取得する意図があるかもしれませんし、再帰関数である可能性もあります。 そのため、自分を参照している場合は参照先から外すということも考えましたが、やめておきました。

関数の中で複数回呼び出される場合、その回数分だけ参照関係を出力する。#

概要で出力したクラス図をご覧の通り、いくつかの関数から同じ関数に向けて複数本の矢印が伸びているのが確認できるかと思います。 この場合に、矢印の本数を1本にするようにした方が良いのかどうか悩みましたが、矢印の本数を複数本見せることで修正箇所が何箇所あるかどうかも確認できるようになっているので、そのまま、複数本の矢印を描画する状態にしておきました。

これは、先程挙げた「コメントや文字列の中にある関数名も拾ってきてしまう。」の点とも絡むところになります。

他のファイルとの参照関係は取れない。#

あくまで、今回のスクリプトの実装範囲としては、「1つの」ファイル内の参照関係なので、他のファイルからインポートした関数の参照関係は描画できません。 出来たら更に便利なんですけどね・・・

しかし、実装にとても手間がかかりますね。コードの静的解析ツールを作るのってなかなか骨が折れるということが今回分かりました。

モックやフィクスチャが入っていた場合を対応していない。#

主にテストコードが記述されているファイルに使用した場合、モックやフィクスチャが沢山入っていると思うので、そこに対応していないんですよね・・・

参照元の関数を切り替えたら、その宣言部分の行の前後を見ることになるのかな。実装が険しいですねえ・・・

おしまい#

135ml avatar
135ml
ある程度だけでも可視化されていれば役立つなという話でした。
リサちゃん avatar
リサちゃん
ほほう

以上になります!

記事を共有

この記事が役に立ったなら、ぜひ他の人と共有してください!

【Python】1つのファイル内における関数の依存関係をMermaidの書式で出力する
https://endorphinbath.com/posts/python-mermaid-print-dependencies/
著者
kinkinbeer135ml
公開日
2022-09-11
ライセンス
CC BY-NC-SA 4.0
関連記事 スマート
1
【Python】.pyファイルにある関数とメソッドを全て取得する
Code Pythonで.pyファイルの中に記述されている関数およびメソッドを全て取得するスクリプトを掲載します。
2
【Python】Pydanticのvalidatorが非推奨だからfield_validatorを使って2段階バリデーションを実装する
Code @validatorはdeprecatedになってるし、@field_validatorにはpreとかalwaysフラグが無いから、from pydantic.functional_validators import field_validatorでインポートしたfield_validatorを使用する方法と、BeforeValidatorとAfterValidatorをAnnotateの中に入れる実装方法を試した。
3
【Python】pytestで同じディレクトリのモジュールをimportして、"ModuleNotFoundError: No module named"を出さなくする
Code Pythonスクリプトをpytestするとき、"ModuleNotFoundError: No module named"が表示されてしまった場合、この記事の方法でそのエラーが解決するかも。
4
【Python】複数の区切り文字を指定して文字列を配列に分割する
Code Pythonで文字列を配列に分割するスクリプトを掲載します。分割文字は配列で指定するように作っています。
5
【Python】文字列の先頭と末尾にあるスペース、空白文字を削除する
Code Pythonで文字列の先頭と末尾にスペース(空白文字)が混じっていることがあります。そのスペースを削除するスクリプトを掲載します。
ランダム記事 ランダム
Profile Image of the Author
kinkinbeer135ml
SIerをやめて、プログラミングを勉強しています。※Amazonアソシエイトに参加しています。
お知らせ
私のブログへようこそ!これはサンプルのお知らせです。
音楽
カバー

音楽

再生中なし

0:00 0:00
歌詞なし
カテゴリ
タグ
サイト統計
記事
287
カテゴリー
8
タグ
93
総文字数
486,174
運用日数
0
最終活動
0 日前

目次