【入門編】Poetryのプラグイン開発入門:独自の依存関係バリデーションを追加してチームの品質を担保する – ビルド・パッケージ管理ツール生産性向上バイブル

こんにちは!日々の開発、本当にお疲れ様です。

Pythonのパッケージ管理、皆さんは何を使っていますか?長年 `pip` と `requirements.txt` の組み合わせで苦労してきたチームにとって、依存関係の解決を美しく自動化してくれる Poetry は、まさに救世主のような存在ですよね。

さて、チーム開発の規模が大きくなると、こんな悩みが出てきませんか?
「うっかりセキュリティ脆弱性のある古いバージョンのライブラリを指定してしまった」
「社内規定で禁止されているライセンスのパッケージが混入してしまった」

これらをCI/CDのパイプライン(GitHub Actionsなど)で検知するのも大事ですが、「そもそも開発者のローカルPCで `poetry add` や `poetry install` を実行した瞬間に弾いてくれたら、どれだけ楽か」と思いませんか?

実は、Poetryには強力なプラグイン機構が備わっています。今回は、Poetryの内部挙動にフックし、チーム独自の依存関係ポリシー(ルール)を強制する「カスタムプラグイン」の作り方を、誰よりも分かりやすく解説します。

これをマスターすれば、あなたのチームのコード品質の「防波堤」が手に入り、レビュー時の無駄な指摘が劇的に減りますよ。一緒に手を動かしてみましょう!

—

1. Poetryプラグインの仕組みとアーキテクチャ

Poetryのプラグインは、Pythonの標準機能である `importlib.metadata`(エントリーポイント)の仕組みを利用して動いています。

Poetryが起動する際、インストールされているプラグインを動的にスキャンし、特定のイベント(コマンドの実行前など)に独自の処理を割り込ませます(これをイベントリスナーやフックと呼びます)。

今回は、`poetry add` や `poetry install` が実行された直後に、`pyproject.toml` に記述されている依存関係を読み込み、「特定の禁止パッケージが含まれていないか」を検証するプラグインを作ります。

—

2. 開発環境のセットアップとプロジェクト構造

まずは、プラグイン自体を開発するための環境を作ります。プラグインも独立したPythonパッケージとして作成し、Poetryに読み込ませる形をとります。

プラグイン用プロジェクトの作成

任意のディレクトリで、新しいPoetryプロジェクトを立ち上げます。ここでは `poetry-plugin-guardian` という名前にしましょう。

プラグイン用のPoetryプロジェクトを新規作成
poetry new poetry-plugin-guardian

ディレクトリへ移動
cd poetry-plugin-guardian

作成されたプロジェクトのディレクトリ構造を、以下のように整えていきます。

poetry-plugin-guardian/
├── README.md
├── poetry.lock
├── pyproject.toml
└── poetry_plugin_guardian
├── __init__.py
└── plugin.py # ここにプラグインのロジックを書きます

—

3. pyproject.toml の設定(ここが最重要!)

Poetryのプラグインとして認識させるためには、`pyproject.toml` の設定に秘密があります。通常のプロジェクト設定に加え、「エントリーポイント」を定義する必要があります。

以下の内容で `pyproject.toml` を書き換えてください。

[tool.poetry]
name = “poetry-plugin-guardian”
version = “0.1.0”
description = “チームの依存関係を監視するカスタムPoetryプラグイン”
authors = [“Your Name “]
packages = [{include = “poetry_plugin_guardian”}]

[tool.poetry.dependencies]
python = “^3.8”
依存関係としてpoetry本体を指定(プラグイン開発に必須)
poetry = “^1.2.0”

[tool.poetry.plugin]
Poetryに「これはプラグインですよ」と教えるためのエントリーポイント
guardian = “poetry_plugin_guardian.plugin:GuardianPlugin”

[build-system]
requires = [“poetry-core>=1.0.0”]
build-backend = “poetry.core.masonry.api”

ここがポイント:
`[tool.poetry.plugin]` セクションにある `guardian = “poetry_plugin_guardian.plugin:GuardianPlugin”` が、Poetryにこのプラグインを認識させる魔法の呪文です。Poetryは起動時にこの定義を読み込み、指定されたクラスをインスタンス化します。

—

4. 独自のバリデーションロジックを書く(HelloWorldの実装)

それでは、実際にプラグインのコードを書いていきましょう。
`poetry_plugin_guardian/plugin.py` を開き、以下のコードを記述してください。

from poetry.plugins.plugin import Plugin
from poetry.poetry import Poetry
from cleo.events.event_dispatcher import EventDispatcher
from cleo.events.console_events import COMMAND
from cleo.events.console_command_event import ConsoleCommandEvent
from poetry.console.application import Application
import sys

class GuardianPlugin(Plugin):
“””
Poetryの起動時に呼び出されるプラグインのエントリーポイントクラス。
“””
def activate(self, poetry: Poetry, application: Application) -> None:
# イベントディスパッチャを取得し、コマンド実行前のイベントにフックを設定する
application.event_dispatcher.add_listener(COMMAND, self.validate_dependencies)

def validate_dependencies(self, event: ConsoleCommandEvent, event_name: str, dispatcher: EventDispatcher) -> None:
“””
Poetryのコマンドが実行される直前に走るバリデーション関数
“””
command = event.command

# 今回は ‘add’ または ‘install’ コマンドが実行された時だけチェックする
if command.get_name() not in [“add”, “install”]:
return

# pyproject.tomlに定義されている依存関係を取得
# (実際にはここでpoetryオブジェクトから依存関係ツリーを解析します)
print(“\n[GuardianPlugin] 🛡️ 依存関係のセキュリティ&ポリシーチェックを実行中…”)

# — 【デモ用のバリデーションルール】 —
# 例として、セキュリティ上の理由で禁止したいパッケージ名リスト
forbidden_packages = [“unsafe-lib”, “legacy-parser”]

# 簡易的に現在のプロジェクトの依存関係を取得してチェック
requires = event.command.poetry.package.requires

for dependency in requires:
if dependency.name in forbidden_packages:
print(f”\n[ERROR] 🚫 禁止されているパッケージ ‘{dependency.name}’ が検出されました!”)
print(“チームのセキュリティポリシーにより、このパッケージの導入は許可されていません。”)
# 異常終了させてコマンドの実行をブロックする
sys.exit(1)

print(“[GuardianPlugin] ✅ チェック完了:ポリシー違反はありません。\n”)

このコードでは、開発者が `poetry add` や `poetry install` を叩いた瞬間、コマンドが実行される一瞬前にイベントを割り込ませ(`COMMAND`イベント)、禁止されたパッケージが含まれていないかをチェックしています。含まれていた場合は `sys.exit(1)` で強制終了させ、不正な依存関係の侵入を防ぎます。

—

5. プラグインをローカル環境にインストールして動作確認

自分で作ったプラグインを、Poetryに認識させてみましょう。
プラグイン開発時は、開発中のパッケージをローカルで直接Poetryにインストール(self-install)させると非常にスムーズです。

ターミナルでプラグインのプロジェクトディレクトリを開き、以下のコマンドを実行します。

現在のプロジェクトをPoetryのプラグインとしてローカルに組み込む
poetry self add .

うまくインストールが完了すると、Poetryのプラグインリストにこのプラグインが追加されます。確認してみましょう。

poetry self show plugins

実行ログのイメージ:

poetry-plugin-guardian (0.1.0)

見事に認識されていますね!

動作確認:バリデーションが機能するか試す

別の適当なPythonプロジェクト(またはテスト用プロジェクト)に移動し、わざと禁止されているパッケージ(例: `unsafe-lib`)を追加してみましょう。

poetry add unsafe-lib

期待される出力結果:

[GuardianPlugin] 🛡️ 依存関係のセキュリティ&ポリシーチェックを実行中…

[ERROR] 🚫 禁止されているパッケージ ‘unsafe-lib’ が検出されました!
チームのセキュリティポリシーにより、このパッケージの導入は許可されていません。

お見事です!`poetry add` が実行される前にプラグインが割り込み、エラーメッセージを出力して処理を完全にストップさせることができました。

—

6. さらに実務で活かすためのアーキテクトからの助言

今回作成したプラグインは非常にシンプルなものですが、実務の現場では以下のような拡張を行うことで、さらに強力なツールへと進化させることができます。

1. 外部APIとの連携:
社内の脆弱性データベースや、CVE(共通脆弱性情報)のAPIに非同期で問い合わせ、最新の脆弱性があるバージョンを指定していないかを動的にチェックする。
2. ライセンス監査:
依存パッケージのライセンス(MIT, GPL, Apache-2.0など)をメタデータから自動検出し、社内法務が許可していないライセンスが含まれていたら弾く。
3. チーム間での共有:
このプラグイン自体をプライベートなGitリポジトリや社内PyPIサーバーでホストし、`poetry self add git+https://…` のようにしてチームメンバー全員のPCに一括導入する。

—

まとめ

今回は、Poetryのプラグイン機構を利用して、独自の依存関係バリデーションツールを作る方法を解説しました。

  • Poetryのイベントフックを使えば、コマンド実行前に独自のチェックを挟むことができる。
  • `pyproject.toml` の `[tool.poetry.plugin]` でエントリーポイントを定義する。
  • CI/CDに頼る前にローカルの `poetry add` などの段階で防ぐことで、手戻りをゼロにできる。

これをマスターすれば、あなたのチームのコード品質とセキュリティは一段上のステージへと引き上げられます。ぜひ、あなたのチーム独自のルールをコード化して、快適で安全なPython開発環境を作ってみてくださいね!

タイトルとURLをコピーしました