mcpField
P
python-resource-management

python-resource-management

github.com/wshobson開発41.9k11.1k 回インストール2 週間前に更新
リポジトリ

python-resource-management について

データベース接続、ファイル、ソケットなどを確実に解放するためのPythonスキルです。同期・非同期コンテキストマネージャーや例外時の動作、再利用可能なクリーンアップ手法を開発者向けに解説します。

主な機能

  1. 01同期コンテキストマネージャーの利用
  2. 02非同期コンテキストマネージャーの実装
  3. 03例外後もリソースを解放
  4. 04ネストしたリソースのクリーンアップ
  5. 05contextlibデコレーターで管理器を作成

活用シーン

  1. 01データベース接続とプールの管理
  2. 02エラー後にファイルハンドルを確実に閉じる
  3. 03状態を持つストリーミング応答の構築
  4. 04複数のリソースを動的にクリーンアップ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshobson/agents --skill python-resource-management

インストール後、Claude Code で/python-resource-managementを入力して読み込まれたか確認します。

同梱ファイルreferences/details.md

詳細

Python リソース管理

コンテキストマネージャーを使用してリソースを確実に管理します。データベース接続、ファイルハンドル、ネットワークソケットなどのリソースは、例外が発生した場合でも確実に解放される必要があります。

このスキルを使用する場面

  • データベース接続と接続プールの管理
  • ファイルハンドルと I/O の操作
  • カスタムコンテキストマネージャーの実装
  • 状態を持つストリーミングレスポンスの構築
  • ネストされたリソースのクリーンアップの処理
  • 非同期コンテキストマネージャーの作成

コアコンセプト

1. コンテキストマネージャー

with 文は、例外が発生した場合でもリソースが自動的に解放されることを保証します。

2. プロトコルメソッド

同期には __enter__/__exit__、非同期のリソース管理には __aenter__/__aexit__ を使用します。

3. 無条件のクリーンアップ

__exit__ は、例外が発生したかどうかに関係なく、常に実行されます。

4. 例外処理

__exit__ から True を返すと例外を抑制し、False を返すと伝播させます。

クイックスタート

from contextlib import contextmanager

@contextmanager
def managed_resource():
    resource = acquire_resource()
    try:
        yield resource
    finally:
        resource.cleanup()

with managed_resource() as r:
    r.do_work()

基本的なパターン

パターン 1: クラスベースのコンテキストマネージャー

複雑なリソースには、コンテキストマネージャープロトコルを実装します。

class DatabaseConnection:
    """Database connection with automatic cleanup."""

    def __init__(self, dsn: str) -> None:
        self._dsn = dsn
        self._conn: Connection | None = None

    def connect(self) -> None:
        """Establish database connection."""
        self._conn = psycopg.connect(self._dsn)

    def close(self) -> None:
        """Close connection if open."""
        if self._conn is not None:
            self._conn.close()
            self._conn = None

    def __enter__(self) -> "DatabaseConnection":
        """Enter context: connect and return self."""
        self.connect()
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        """Exit context: always close connection."""
        self.close()

# Usage with context manager (preferred)
with DatabaseConnection(dsn) as db:
    result = db.execute(query)

# Manual management when needed
db = DatabaseConnection(dsn)
db.connect()
try:
    result = db.execute(query)
finally:
    db.close()
パターン 2: 非同期コンテキストマネージャー

非同期リソースには、非同期プロトコルを実装します。

class AsyncDatabasePool:
    """Async database connection pool."""

    def __init__(self, dsn: str, min_size: int = 1, max_size: int = 10) -> None:
        self._dsn = dsn
        self._min_size = min_size
        self._max_size = max_size
        self._pool: asyncpg.Pool | None = None

    async def __aenter__(self) -> "AsyncDatabasePool":
        """Create connection pool."""
        self._pool = await asyncpg.create_pool(
            self._dsn,
            min_size=self._min_size,
            max_size=self._max_size,
        )
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        """Close all connections in pool."""
        if self._pool is not None:
            await self._pool.close()

    async def execute(self, query: str, *args) -> list[dict]:
        """Execute query using pooled connection."""
        async with self._pool.acquire() as conn:
            return await conn.fetch(query, *args)

# Usage
async with AsyncDatabasePool(dsn) as pool:
    users = await pool.execute("SELECT * FROM users WHERE active = $1", True)
パターン 3: @contextmanager デコレーターの使用

単純なケースでは、デコレーターを使用してコンテキストマネージャーを簡素化します。

from contextlib import contextmanager, asynccontextmanager
import time
import structlog

logger = structlog.get_logger()

@contextmanager
def timed_block(name: str):
    """Time a block of code."""
    start = time.perf_counter()
    try:
        yield
    finally:
        elapsed = time.perf_counter() - start
        logger.info(f"{name} completed", duration_seconds=round(elapsed, 3))

# Usage
with timed_block("data_processing"):
    process_large_dataset()

@asynccontextmanager
async def database_transaction(conn: AsyncConnection):
    """Manage database transaction."""
    await conn.execute("BEGIN")
    try:
        yield conn
        await conn.execute("COMMIT")
    except Exception:
        await conn.execute("ROLLBACK")
        raise

# Usage
async with database_transaction(conn) as tx:
    await tx.execute("INSERT INTO users ...")
    await tx.execute("INSERT INTO audit_log ...")
パターン 4: 無条件のリソース解放

例外に関係なく、__exit__ で常にリソースをクリーンアップします。

class FileProcessor:
    """Process file with guaranteed cleanup."""

    def __init__(self, path: str) -> None:
        self._path = path
        self._file: IO | None = None
        self._temp_files: list[Path] = []

    def __enter__(self) -> "FileProcessor":
        self._file = open(self._path, "r")
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        """Clean up all resources unconditionally."""
        # Close main file
        if self._file is not None:
            self._file.close()

        # Clean up any temporary files
        for temp_file in self._temp_files:
            try:
                temp_file.unlink()
            except OSError:
                pass  # Best effort cleanup

        # Return None/False to propagate any exception

詳細な実例とパターン

詳細なセクション(## Advanced Patterns で始まるもの)は references/details.md にあります。上記のナビゲーション概要では不十分な場合に、そのファイルを参照してください。

ベストプラクティスのまとめ

  1. 常にコンテキストマネージャーを使用する - クリーンアップが必要なあらゆるリソースに対して
  2. 無条件にクリーンアップする - __exit__ は例外が発生しても実行される
  3. 予期せず抑制しない - 抑制が意図的でない限り False を返す
  4. @contextmanager を使用する - 単純なリソースパターンに対して
  5. 両方のプロトコルを実装する - with と手動管理の両方をサポートする
  6. ExitStack を使用する - 動的な数のリソースに対して
  7. 効率的に蓄積する - 文字列連結ではなく、リスト + join を使用する
  8. メトリクスを追跡する - ストリーミングでは初回バイトまでの時間が重要
  9. 動作を文書化する - 特に例外の抑制について
  10. クリーンアップの経路をテストする - エラー時にリソースが解放されることを確認する

コメント