Slide 1

Slide 1 text

Pylint Custom Ruleで始め るレビュー自動化 エムスリー株式会社 氏家翔吾 1

Slide 2

Slide 2 text

● 氏家 翔吾(@mowmow1259) ● エムスリー株式会社 AI・機械学習チーム リーダー ○ 2021年新卒入社 ○ 15人のチームメンバー ● 福岡オフィス所属 ● ML/MLOpsを業務で触っている 2 自己紹介

Slide 3

Slide 3 text

● Linterの独自ルールを使って業務を効率化した話 ● pyLintの独自ルール実装 ● pylintの内部実装・tips 3 今日話すこと Linterで解決できそうな身近な問題がわかる pylintの独自ルールを開発できる

Slide 4

Slide 4 text

今日作れるようになるもの: 任意のルールに沿ったlinter エディタのLinterポップアップ この機能/書き方使わないでね、を人間の代わりに Linterがレビュー

Slide 5

Slide 5 text

5 ブログも是非ご覧ください https://www.m3tech.blog/entry/2024/10/02/110000

Slide 6

Slide 6 text

(※) 2023年10月時点 日本の医師のエムスリー会員率 エムスリーが事業展開している国の数 エムスリーが占める全世界で医師会員の割合 全世界で医師会員合計 17 カ国 (※) 50 %以上 (※) 650 万人以上 (※) 90 %以上 エムスリー が展開する医療従事者向け情報 サイト「m3.com」は32万人を突破、日本 の医師の9割以上が会員。(※) 日本の医師の9割が登録するエムスリーのサービス グローバルでも医師の5割以上が会員、医療業界に根付く事業基盤 圧倒的な医療データへのアクセス容易性を活かしたAI開発 6

Slide 7

Slide 7 text

エムスリーのプロダクトに根ざしたAI技術活用事例 ToB・ToC・グローバルプロダクトにも展開 医療の疑問に医師が答えるAskDoctors 検索エンジン開発 海外版m3.comの記事やQuizのレコメンド AIチームメンバーがUS出張開始 シェアNo.1のクラウド電子カルテ 病名入力補完APIの開発 医療業界のビッグデータを活用 マーケティング向けデータ分析 医療画像やウェアラブルデバイスデータを活用 医療業界のAI活用を推進 7

Slide 8

Slide 8 text

エムスリーのプロダクトに根ざしたAI技術活用事例 ToB・ToC・グローバルプロダクトにも展開 医療の疑問に医師が答えるAskDoctors 検索エンジン開発 海外版m3.comの記事やQuizのレコメンド AIチームメンバーがUS出張開始 シェアNo.1のクラウド電子カルテ 病名入力補完APIの開発 医療業界のビッグデータを活用 マーケティング向けデータ分析 医療画像やウェアラブルデバイスデータを活用 医療業界のAI活用を推進 8 大多数をPythonで 開発してます!

Slide 9

Slide 9 text

9 AI・機械学習チームのPythonを使った開発 ● チームができて7年、ほとんどの機械学習プロダクトをPythonで開発 ○ 推薦モデルの開発 ○ 画像診断モデルの開発 ● そのほぼすべてをgokartというOSSを使って開発 ○ エムスリーが主に開発しているpythonの機械学習パイプラインのOSS 徐々に開発上のベストプラクティスが蓄積

Slide 10

Slide 10 text

10 ベストプラクティスの蓄積 この機能 罠多いから使わない 方がいいな Pythonのこの書き 方しない方がいい よ

Slide 11

Slide 11 text

11 技術共有会によるチームメンバーへの周知 技術共有会 ○ 毎週1時間のチーム全員参加会 ○ 開発上の知見を発表し合う この機能 罠多いから使わない 方がいいな この機能 使わない方がいいっ すよ Pythonのこの書き 方しない方がいい よ

Slide 12

Slide 12 text

12 レビューによる実践 この機能 使わない方がいいら しいっすよ あぁー、言ってました ね〜

Slide 13

Slide 13 text

これでよさそう? 13

Slide 14

Slide 14 text

次の日 14

Slide 15

Slide 15 text

15 前も言った通り、 この機能 使わない方がいいっ すよ あぁー、そうでしたそう でした ※ フィクションです レビューによる実践

Slide 16

Slide 16 text

次の日 16

Slide 17

Slide 17 text

17 ※ フィクションです あぁー、そんなことも 言ってましたね〜 この機能... 使わない方が ... いいらしいっすよ ... レビューによる実践

Slide 18

Slide 18 text

18 ※ フィクションです あぁー、そんなことも 言ってましたね〜 この機能... 使わない方が ... いいらしいっすよ ... レビューによる実践

Slide 19

Slide 19 text

19 レビューによる実践の難しさ ● 人間のコードレビューでの周知は運用が難しい ● 定型の指摘はやる方もやられる方もしんどくなりがち ● 新メンバーへの共有も課題 ○ 技術共有会を聞いてないので背景がわからない レビュー漏れ レビュー負荷増加

Slide 20

Slide 20 text

自動化すればいいじゃん! 20

Slide 21

Slide 21 text

21 Linterに組み込んでレビューを自動化 ● Linterの独自ルールを実装し、CIに組み込み ● ベストプラクティスに沿ってないコードは自動的に落ちる

Slide 22

Slide 22 text

22 自動化によるメリット 人を介さず修正を強制 エディタへの組み込み による開発効率アップ Lintの警告文による 背景の共有 レビュー負荷は0 レビュー漏れもなし 開発時に違反が即わかる → 手戻りなく開発可能 技術共有会を聞いていないメンバーにも 警告文で背景を共有

Slide 23

Slide 23 text

いいことづくめ 23

Slide 24

Slide 24 text

Linterの独自ルール開発 24

Slide 25

Slide 25 text

25 PythonでのLinter候補 ruff ○ デファクトとなりつつあるRust製のPython Linter ○ 弊チームでも基本的にはruffを使っている ○ 独自ルールや外部Pluginの差し込みは不可能 pylint ○ vscodeの標準Python Linter ○ (おそらく)Python Linter界で最もstarが多い ○ 独自ルールの追加が容易 flake8 ○ 言わずと知れたPython Linter ○ 使い勝手はほぼpylintと同じ

Slide 26

Slide 26 text

26 PythonでのLinter候補 ruff ○ デファクトとなりつつあるRust製のPython Linter ○ 弊チームでも基本的にはruffを使っている ○ 独自ルールや外部Pluginの差し込みは不可能 pylint ○ vscodeの標準Python Linter ○ (おそらく)Python Linter界で最もstarが多い ○ 独自ルールの追加が容易 flake8 ○ 言わずと知れたPython Linter ○ 使い勝手はほぼpylintと同じ

Slide 27

Slide 27 text

27 Pylintの導入

Slide 28

Slide 28 text

28 Pylintでの独自ルールはクラスを一つ作るだけ! Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 例として「変数名がhogeのときに違 反となるルール」を作って行きます

Slide 29

Slide 29 text

29 独自ルール例: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 ルールを定義

Slide 30

Slide 30 text

30 独自ルール例: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 コールバック関数の実装 変数名がhogeだった場合に hoge-assign違反として通知

Slide 31

Slide 31 text

31 独自ルール例: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行

Slide 32

Slide 32 text

32 独自ルール例: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 さっきのruleをsample-ruleパッケージとして読み込み、 hoge-assignルールを有効化

Slide 33

Slide 33 text

33 独自ルール例: 変数”hoge”の禁止 hogeを違反として検知!

Slide 34

Slide 34 text

基本的な流れはこれだけ! 34

Slide 35

Slide 35 text

発展: pylintの内部実装 35

Slide 36

Slide 36 text

36 PyLintの仕組み 1. 抽象構文木(AST)に分解 2. ASTを走査 3. 各ノードでルールをチェック

Slide 37

Slide 37 text

37 1. 抽象構文木(AST)に分解 ● 抽象構文木 = コードの各情報をノードとした木構造

Slide 38

Slide 38 text

38 1. 抽象構文木(AST)に分解 ● 抽象構文木 = コードの各情報をノードとした木構造 Module Assign Name BinOp Constant Constant Add i = 1 + 2 変数(i) 演算 (1 + 2) 1 + 2

Slide 39

Slide 39 text

39 2. ASTを走査 ● PyLinter(が持つASTWalker)がノードを深さ優先探索していく Module Assign Name BinOp Constant Constant Add Pylinter

Slide 40

Slide 40 text

40 2. ASTを走査 Module Assign Name BinOp Constant Constant Add Pylinter ● PyLinter(が持つASTWalker)がノードを深さ優先探索していく

Slide 41

Slide 41 text

41 2. ASTを走査 Module Assign Name BinOp Constant Constant Add Pylinter ● PyLinter(が持つASTWalker)がノードを深さ優先探索していく

Slide 42

Slide 42 text

42 2. ASTを走査 Module Assign Name BinOp Constant Constant Add Pylinter ● PyLinter(が持つASTWalker)がノードを深さ優先探索していく

Slide 43

Slide 43 text

43 3. 各ノードでルールをチェック ● 各ノード探索時に、Checkerのコールバックを呼び出す Module Assign Name BinOp Constant Constant Add Pylinter

Slide 44

Slide 44 text

44 3. 各ノードでルールをチェック ● 各ノード探索時に、Checkerのコールバックを呼び出す Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker visit_module コードに違反があった場合 visit_moduleがそれを pylintに返す ルール

Slide 45

Slide 45 text

45 3. 各ノードでルールをチェック ● 各ノード探索時に、Checkerのコールバックを呼び出す Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker visit_assign

Slide 46

Slide 46 text

46 3. 各ノードでルールをチェック ● 各ノード探索時に、Checkerのコールバックを呼び出す Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker visit_binop

Slide 47

Slide 47 text

47 Checkerとして独自ルールを追加する ● 独自ルールを記述したコールバック関数を持つCheckerを実装すればよい Pylinter Checker Checker Checker Checker visit_* HogeAssign Checker visit_name

Slide 48

Slide 48 text

48 独自ルールのコールバック関数を呼び出し Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker HogeAssign Checker 変数(i)

Slide 49

Slide 49 text

49 独自ルールのコールバック関数を呼び出し Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker HogeAssign Checker 変数(i)

Slide 50

Slide 50 text

50 独自ルールのコールバック関数を呼び出し Module Assign Name BinOp Constant Constant Add Pylinter Checker Checker Checker Checker HogeAssign Checker visit_name 変数(i)

Slide 51

Slide 51 text

51 再掲: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 pylinterに登録するCheckerクラス

Slide 52

Slide 52 text

52 再掲: 変数”hoge”の禁止 Checkerクラスを 作成 Checkerクラスを 登録 有効化して実行 nameノード訪問時のコールバック関数

Slide 53

Slide 53 text

独自ルール開発時のtips 53

Slide 54

Slide 54 text

54 その①: nestを検知したい 三重ループ目を禁止したい

Slide 55

Slide 55 text

55 その①: nestを検知したい(ASTに分解) Module For For For

Slide 56

Slide 56 text

56 その①: nestを検知したい(Forを検知) Module For For For Pylinter DeepFor Checker

Slide 57

Slide 57 text

57 Module For For For Pylinter DeepFor Checker その①: nestを検知したい(Forを検知)

Slide 58

Slide 58 text

58 Module For For For Pylinter DeepFor Checker その①: nestを検知したい(Forを検知)

Slide 59

Slide 59 text

59 Module For For For Pylinter DeepFor Checker このForが三つ目の入れ子 だった場合に警告したい その①: nestを検知したい(Forを検知)

Slide 60

Slide 60 text

60 Module For For For Pylinter DeepFor Checker このForが三つ目の入れ子 だった場合に警告したい Forは自分が入れ子のい くつ目かの情報は持って いない その①: nestを検知したい(Forを検知)

Slide 61

Slide 61 text

61 Module For For For Pylinter DeepFor Checker forの訪問時にpush、帰還時 にpopするstackを用意 その①: nestを検知したい(stackを導入)

Slide 62

Slide 62 text

62 Module For For For Pylinter DeepFor Checker push その①: nestを検知したい(stackを導入)

Slide 63

Slide 63 text

63 Module For For For Pylinter DeepFor Checker push その①: nestを検知したい(stackを導入)

Slide 64

Slide 64 text

64 Module For For For Pylinter DeepFor Checker push その①: nestを検知したい(stackを導入)

Slide 65

Slide 65 text

65 Module For For For Pylinter DeepFor Checker nestしている場合、popされず for_stackに3つ溜まっているはず その①: nestを検知したい(stackを導入)

Slide 66

Slide 66 text

66 Module For For For Pylinter DeepFor Checker pop その①: nestを検知したい(stackを導入)

Slide 67

Slide 67 text

67 Module For For For Pylinter DeepFor Checker さっきpopしたので for_stackは2個しか たまっていない その①: nestを検知したい(stackを導入)

Slide 68

Slide 68 text

68 その②: importの解決 ● あるモジュールのある関数の使用を検知したい hogeモジュールのsample関数を禁止したい

Slide 69

Slide 69 text

69 その②: importの解決(ASTに分解) ● あるモジュールのある関数の使用を検知したい Module Import From Expr Call from hoge import sample sample

Slide 70

Slide 70 text

70 その②: importの解決(Callをチェック?) Callの名前がSample かどうかをチェック? Module Import From Expr Call Pylinter Sample Checker

Slide 71

Slide 71 text

71 その②: importの解決(Callをチェック?) Callの名前がSample かどうかをチェック? Module Import From Expr Call Pylinter Sample Checker Callはimport元の情報は 持っていない

Slide 72

Slide 72 text

72 その②: importの解決(解決策) Module Import From Expr Call Pylinter Sample Checker importに訪問時に モジュールの情報を記録

Slide 73

Slide 73 text

73 その②: importの解決(解決策) Module Import From Expr Call Pylinter Sample Checker sampleをimportしているか もチェック

Slide 74

Slide 74 text

他の色々なtipsは公式レポジトリの実装例で 74 https://github.com/pylint-dev/pylint/tree/main/pylint/checkers

Slide 75

Slide 75 text

結局入れてどうだった? ● レビューでよく現れていたベストプラクティス警察は激減 ○ 人間によるレビューは0に ● 新メンバーもベストプラクティスに沿ったコードを自然にかけるように ● レビュワーの認知負荷も軽減

Slide 76

Slide 76 text

76 まとめ ● Linterの独自ルールでベストプラクティスを周知した事例を紹介 ● PyLintでの具体的な実装 ● 意外と簡単に実践できるので、試してみてください!

Slide 77

Slide 77 text

77 We are hiring! ● エムスリーは福岡オフィスでも積極採用中です! ● まずはカジュアル面談から是非!