【Python】追加ライブラリがあるときだけ機能を有効にする:任意依存の扱い

PythonのTopに戻る


追加ライブラリが必要な機能だけを任意に使わせたい場合は、その機能を呼ぶときまでimportを遅らせる。さらに、追加ライブラリそのものがない場合と、その内部で別の読み込みが壊れた場合を分ける。広いImportErrorをまとめて捕まえると、導入済みライブラリの不具合まで「未インストール」と誤案内してしまう。

基本機能まで追加依存に巻き込まない

集計は標準ライブラリだけで行え、グラフ機能だけに追加パッケージが必要なプログラムを考える。トップレベルでそのパッケージをimportすると、グラフを使わない人も読み込み段階で失敗する。任意機能の関数内で読み込めば、基本機能を独立して利用できる。

ただし、単にtry/except ImportErrorで囲むだけでは足りない。外部ライブラリは見つかったが、その内部が必要とする依存先だけ不足している場合もある。その状況を「任意ライブラリを入れてください」に変換すると、すでに入っているものを何度も入れ直すことになる。

例外のnameを調べる

次の例ではtip_optional_plotという練習用の追加機能を想定している。これは実在の製品名を推測してインストールさせるための名前ではない。実際のプログラムでは、利用する配布物の公式手順に沿った案内文へ変更する。

optional_feature.py

import importlib


def total(values):
    return sum(values)


def draw(values):
    try:
        plugin = importlib.import_module("tip_optional_plot")
    except ModuleNotFoundError as exc:
        if exc.name != "tip_optional_plot":
            raise
        raise RuntimeError("Optional plotting feature is not installed") from exc
    return plugin.render(values)

ModuleNotFoundErrorのnameが要求したトップレベル名と同じ場合だけ、利用者向けのRuntimeErrorへ変換する。それ以外はraiseだけで元の例外を再送出するため、失敗した依存先とトレースバックが残る。元の例外をfrom excでつなぐことも、原因を追う助けになる。

未導入・正常・内部の不足を確認する

以下は同じ関数を三つの状態で実行した結果である。追加機能がない状態、render関数を持つ正常な練習用モジュールがある状態、モジュール内部で別の不足名をimportする状態を一時フォルダーに作って確認した。

実行結果

missing: RuntimeError
available: plot:1,2
broken: tip_missing_backend_2026

内部依存の不足が、案内用のRuntimeErrorへ変わっていないことがポイントである。また、追加機能がない場合でもtotalは使える。必要な場所だけで読み込む設計の目的は、単にエラー表示を短くすることではなく、機能ごとの依存を分けることにある。

tryの範囲を小さくする

例ではtryの中にimportだけを置き、描画関数の実行は外側へ出した。もし実行中の処理まで囲むと、関数内部で発生した別のModuleNotFoundErrorを同じ不足判定へ巻き込む可能性がある。読み込み時の案内と、機能が実際に動くときの例外を区別した方が分かりやすい。

name属性による判定は実用的な目安だが、あらゆるライブラリの独自例外に対応する万能判定ではない。ライブラリ自身が任意依存をどのように扱っているかも確認する。要求名にドットを含む場合や、トップレベルと追加機能の配布が分かれている場合は、その構造に合わせた条件が必要になる。

見つかることと動くことは別である

find_specなどで事前に存在を調べても、内部の依存関係やバイナリ互換性まで保証できない。存在確認の直後に状態が変わる可能性もあるため、実際のimportで起こる失敗は適切に扱う必要がある。単純な有無のフラグをプログラム全体へ固定してしまう前に、使用時に確認できないかを考えたい。

また、ImportError以外にも、初期化時の設定不足や拡張モジュールの問題が異なる例外で表れることがある。この例はそれらを捕捉しないため、元のエラーをそのまま調べられる。異常を何でも空の結果へ置き換えると、利用者が正常終了だと誤解してしまう。

利用者に必要な操作だけを案内する

配布するライブラリなら、追加機能ごとの依存をextrasなどでまとめることもできる。その場合は、自分の配布名とextras名を正式に定義したうえで案内する。import名と配布名が同じとは限らないので、例外に出た名前をそのままコマンドへ埋め込まないようにする。

正常時に戻り値が得られることだけでなく、追加依存なしでも基本機能が使えること、内部の不具合が隠れないことを確認しよう。任意機能を後から追加するときも、この三つの状態を分けて考えると、既存機能への影響を小さくできる。

動作確認と参考資料

掲載例はLinux・CPython 3.12.14で動作確認した。OS固有のコマンドや環境ごとに変わるパスは、本文中の条件を確認して使ってほしい。

関連するTips


PythonのTopに戻る