型情報の配布

スタブファイル

(元々 PEP 484 で指定されています。)

スタブファイル、または タイプスタブ は、型情報を持たない Python パッケージおよびモジュールの型情報を提供します。 スタブファイルは複数の目的を果たします:

  • 拡張モジュールに型情報を追加する唯一の方法です。

  • インラインで型情報を追加したくないパッケージに型情報を提供できます。

  • 提供する型のパッケージまたはモジュールとは別に配布できます。 後者は 実装 と呼ばれます。 これにより、スタブを異なるペースで、または異なる著者によって開発することができ、特に既存のパッケージに型注釈を追加する場合に便利です。

  • 実装の詳細やプライベートメンバーを含まずに、パッケージの外部 API を簡潔に説明するドキュメントとして機能します。

スタブファイルは、以下の サポートされている構文 で説明されているように、Python ソースファイルで使用される構文のサブセットを使用します。 型チェッカーは、そのような構文のみを使用するスタブをエラーなしで解析し、この仕様に矛盾しない方法で解釈する必要があります。 ただし、型チェッカーはこれらの構文すべてのチェックを実装する必要はなく、サポートされていないものを無視することを選択できます。 さらに、型チェッカーはここで説明されていない構文をサポートすることもできます。

モジュールのスタブファイルが見つかった場合、型チェッカーは対応する「実際の」モジュールを読み取らないでください。 詳細については インポート解決順序 を参照してください。

構文

スタブファイルは、.pyi サフィックスを持つ構文的に有効な Python ファイルです。 それらは、実装によってサポートされるすべての Python バージョンで(および CPython プロジェクトによってまだサポートされているバージョンで)解析可能である必要があります(例: ast.parse() を使用して)。 たとえば、type キーワードを使用して型エイリアスを定義することは、Python 3.12 以降の Python パーサーによってのみ受け入れられるため、Python 3.11 以前のバージョンをサポートするスタブはこの構文を使用しないでください。 これにより、標準ライブラリの機能を使用してスタブファイルを解析するために Python で実装された型チェッカーが可能になります。 型チェッカーは、スタブファイルで新しい Python バージョンの構文機能をサポートすることを選択できますが、そのような機能に依存するスタブはすべての型チェッカーに移植可能ではない場合があります。 型チェッカーは、CPython によってもはやサポートされていない Python バージョンをサポートすることも選択できます。 その場合、スタブファイルを解析するために標準ライブラリの機能に依存することはできません。

型チェッカーは、すべての annotation expressions を引用符で囲まれているかのように評価する必要があります。 したがって、前方参照を引用符で囲む必要はなく、Python 構文の変更に依存しない型システム機能は、サポートされる Python バージョンに関係なくスタブでサポートされます。 たとえば、共用体を作成するための | 演算子の使用(X | Y)は Python 3.10 で導入されましたが、Python 3.9 およびそれ以前のバージョンをサポートするスタブでも使用できます。

サポートされている構文

型チェッカーは、これらの構文を完全にサポートする必要があります:

  • 最新リリースの Python バージョンの typing モジュールのすべての機能(supported syntax を使用)

  • コメント、型宣言(# type: X)およびエラー抑制(# type: ignore)コメントを含む

  • 標準の インポートの慣例 および循環インポートを含むインポート文

  • モジュールおよびクラスレベルのエイリアスを含むエイリアス

  • Simple version and platform checks

次の小節の構文は、以下に説明するように、より限定的にサポートされる場合があります。

値の式

代入文の右辺や関数パラメータのデフォルトなど、値の式が現れる場所では、型チェッカーは次の式をサポートする必要があります:

  • 任意の値の代わりに使用できる省略記号リテラル ...

  • legal parameter for typing.Literal である任意の値

  • 浮動小数点リテラル(例:3.14

  • 複素数リテラル(例:1 + 2j

モジュールレベルの属性

型チェッカーは、代入の有無にかかわらず、モジュールレベルの変数注釈をサポートする必要があります:

x: int
x: int = 0
x = 0  # type: int
x = ...  # type: int

Literal shortcut using Final はサポートされる必要があります:

x: Final = 0  # 型は Literal[0]

変数の型が省略されている場合や、割り当てられた値と一致しない場合、型チェッカーの動作は未定義です:

x = 0  # 動作未定義
x: Final = ...  # 動作未定義
x: int = ""  # 動作未定義

クラス

クラス定義の構文は一般的な Python の構文に従いますが、型チェッカーはクラス本体内の次の構文のみを理解することが期待されます:

  • 省略記号リテラル ... は空のクラス本体に使用されます。 クラス本体で pass を使用することは未定義です。

  • インスタンス属性は、上記のモジュールレベルの属性と同じルールに従います。

  • メソッド定義(以下を参照)およびプロパティ。

  • エイリアス。

  • 内部クラス定義。

はい:

class Simple: ...

class Complex(Base):
    read_write: int
    @property
    def read_only(self) -> int: ...
    def do_stuff(self, y: str) -> None: ...
    doStuff = do_stuff
    IntList: TypeAlias = list[int]
    class Inner: ...

関数とメソッド

関数およびメソッド定義は一般的な Python の構文に従います。 省略記号リテラル以外の関数またはメソッド本体を使用することは未定義です:

def foo(): ...  # 互換性あり
def bar(): pass  # 動作未定義

デコレータ

型チェッカーは、typing モジュールで定義されたすべてのデコレータの効果を理解することが期待されており、次の追加のデコレータも理解することが期待されます:

  • classmethod

  • staticmethod

  • property``(.setter`` および .deleter を含む)

  • abc.abstractmethod

  • dataclasses.dataclass

  • warnings.deprecated

  • @typing.dataclass_transform で装飾された関数

Typeshed プロジェクト

typeshed プロジェクト には、標準ライブラリの型スタブ(型チェッカーによってベンダー提供されるか特別に処理される)および独自の型情報を提供しないサードパーティライブラリの型スタブが含まれています(通常は PyPI 経由で配布されます)。 そこに収集されたスタブに関するポリシーは別途決定され、プロジェクトのドキュメントに記載されています。

ライブラリ内の型情報

(元々 PEP 561 で指定されています。)

パッケージで型付けをサポートする動機と方法はさまざまです。 この仕様は、型付けを使用するユーザーが作成したい 3 種類のパッケージを認識しています:

  1. パッケージメンテナは、インラインで型情報を追加したいと考えています。

  2. パッケージメンテナは、スタブを使用して型情報を追加したいと考えています。

  3. サードパーティまたはパッケージメンテナは、パッケージのためのスタブファイルを共有したいと考えていますが、メンテナはそれらをパッケージのソースに含めたくありません。

この仕様は、これらの 3 つのシナリオすべてをサポートし、それらをパッケージングおよびデプロイメントに簡単に追加できるようにすることを目的としています。

この仕様の主要な部分は、パッケージング仕様とモジュール型情報の解決順序です。

型情報のパッケージング

型情報のパッケージングおよび配布をできるだけ簡単かつ容易にするために、既存のフレームワークを通じてパッケージングおよび配布が行われます。

コードの型チェックをサポートしたいパッケージメンテナは、型付けをサポートするパッケージに py.typed という名前のマーカーファイルを追加する必要があります。 このマーカーは再帰的に適用されます: トップレベルパッケージに含まれている場合、そのすべてのサブパッケージも型チェックをサポートする必要があります。

このファイルをパッケージに含めるには、メンテナは setuptoolspackage_data などの既存のパッケージングオプションを使用できます。 詳細については、the guide to providing type annotations を参照してください。

名前空間パッケージ(PEP 420 を参照)では、競合を避けるために、明確にするために、py.typed ファイルは名前空間のサブモジュールに配置する必要があります。

この仕様は、モジュールのみの配布や名前空間パッケージ内の単一ファイルモジュールの一部として型情報を配布することをサポートしていません。

単一ファイルモジュールはパッケージにリファクタリングされ、上記のようにパッケージが型付けをサポートすることを示す必要があります。

スタブのみのパッケージ

すべての型情報を含むスタブファイルを配布したいパッケージメンテナの場合、*.pyi スタブが対応する *.py ファイルと一緒にあることが望ましいです。 ただし、スタブは別のパッケージに配置して別々に配布することもできます。 サードパーティもスタブファイルを配布したい場合、この方法が役立ちます。 スタブパッケージの名前は、foopkg という名前のパッケージの型スタブの場合、foopkg-stubs というスキームに従う必要があります。

パッケージを含む配布物の名前(つまり、PyPI 上のプロジェクト名)は、規定された *-stubs パッケージ名と異なる場合があります。 配布物の名前は types-* であってはなりません。これは、通常、typeshed によって提供されるスタブのみのパッケージに使用されるためです。

スタブのみのパッケージには py.typed マーカーを追加する必要はありません。 *-stubs という名前だけで、型情報のソースであることを示すのに十分です。

スタブファイルを配布しようとするサードパーティは、パッケージのメンテナに連絡して、パッケージと一緒に配布することをお勧めします。 メンテナがスタブファイルや型情報をインラインで維持またはパッケージ化したくない場合、サードパーティのスタブのみのパッケージを作成できます。

さらに、スタブのみの配布物は、通常の依存関係データを通じて、ランタイムパッケージのバージョンを示すことができます。 たとえば、スタブパッケージ flyingcircus-stubs は、dependencies フィールドを通じて、ランタイム flyingcircus 配布物のバージョンを示すことができます。

名前空間パッケージ(PEP 420 を参照)では、スタブのみのパッケージはルート名前空間パッケージにのみ -stubs サフィックスを使用する必要があります。 すべてのスタブのみの名前空間パッケージは __init__.pyi ファイルを省略する必要があります。 スタブのみのパッケージには py.typed マーカーファイルは必要ありませんが、インライン型を持つパッケージと同様に、競合を避けるために名前空間のサブモジュールに配置する必要があります。

たとえば、pentagon および hexagon が名前空間パッケージ shapes.polygons 内にインストールされる別々の配布物である場合、対応する型のみの配布物は次のようにレイアウトされたパッケージを生成する必要があります:

shapes-stubs
└── polygons
    └── pentagon
        └── __init__.pyi

shapes-stubs
└── polygons
    └── hexagon
        └── __init__.pyi

部分的なスタブパッケージ

多くのスタブパッケージは、特に最初はライブラリの型インターフェースの一部しか完成していない場合があります。 型チェックおよびコードエディタの利便性のために、パッケージは「部分的」であることができます。 これは、スタブパッケージに見つからないモジュールが、モジュール解決順序の第 5 部および第 6 部で検索されるべきであることを意味します。 具体的には、inline パッケージおよび型チェッカーがベンダー提供する任意のサードパーティスタブです。

型チェッカーは、スタブパッケージとランタイムパッケージのディレクトリをマージする必要があります。 これは、スタブパッケージを対応するランタイムパッケージと同じディレクトリにコピーし、結合されたディレクトリ構造を型チェックするのと同等の機能と見なすことができます。 したがって、型チェッカーは *.pyi ファイルを *.py ファイルの前にチェックする通常の解決順序を維持する必要があります。

スタブパッケージ配布物が部分的である場合、py.typed ファイルに partial\n を含める必要があります。 名前空間パッケージ内で配布されるスタブパッケージの場合、py.typed ファイルは名前空間のサブモジュールに配置する必要があります。

型チェッカーは、スタブパッケージ内の名前空間パッケージを不完全として扱う必要があります。これは、複数の配布物がそれらを埋める可能性があるためです。 スタブパッケージ配布物内の名前空間パッケージ内の通常のパッケージは、py.typedpartial\n が含まれていない限り、完全と見なされます。

インポート解決順序

この仕様をサポートする型チェッカーは、次の順序で型情報を含むモジュールを解決する必要があります:

  1. 手動でパスの先頭に配置された Stubs または Python ソース。 型チェッカーはこれを提供し、ユーザーが使用するスタブを完全に制御し、壊れたスタブやパッケージの inline 型をパッチすることを許可する必要があります。 mypy では、$MYPYPATH 環境変数を使用できます。

  2. ユーザーコード - 型チェッカーが実行しているファイル。

  3. 標準ライブラリの Typeshed スタブ。 これらは通常、型チェッカーによってベンダー提供されますが、型チェッカーはユーザーがカスタムまたは変更されたバージョンの typeshed を含むディレクトリへのパスを提供するオプションを提供する必要があります。 このオプションが提供される場合、型チェッカーはこのステップで標準ライブラリの型の正規のソースとしてこれを使用する必要があります。

  4. Stub パッケージ - これらのパッケージは、インストールされたインラインパッケージよりも優先される必要があります。 それらは、パッケージ foopkg のために foopkg-stubs という名前のディレクトリに見つけることができます。

  5. py.typed マーカーファイルを持つパッケージ - インストールされたパッケージを上書きするものがなく、型チェックにオプトインしている場合、パッケージにバンドルされた型(.pyi タイプスタブファイルまたは .py ファイル内のインライン型)が使用されるべきです。

  6. 型チェッカーが追加のサードパーティスタブ(typeshed からまたは他の場所から)をベンダー提供することを選択した場合、これらはモジュール解決順序の最後に来るべきです。

型チェッカーがステップ 4 で目的のモジュールを持たないスタブのみの名前空間パッケージを識別した場合、ステップ 5/6 に進む必要があります。 型チェッカーは、__init__.pyi がないことによって名前空間パッケージを識別する必要があります。 これにより、異なるサブパッケージがインライン対スタブのみを独立して選択できるようになります。

実行されているバージョンとは異なる Python バージョンをチェックする型チェッカーは、その Python バージョンの site-packages/dist-packages で型情報を見つける必要があります。 これは、たとえば pythonX.Y -c 'import site; print(site.getsitepackages())' でクエリできます。 型チェッカーがパスにない場合に特定の Python バイナリを指すようにユーザーに許可することも推奨されます。

ライブラリインターフェース(公開および非公開シンボル)

py.typed モジュールが存在する場合、型チェッカーはそのパッケージ内のすべてのモジュール(つまり、.py または .pyi で終わるすべてのファイル)をインポート可能と見なします。 これらのモジュールは、ライブラリのサポートされているインターフェースを構成します。

各モジュールは一連のシンボルを公開します。 これらのシンボルの一部は「非公開」と見なされます。これは、ライブラリのインターフェースの一部ではない実装の詳細です。 型チェッカーは、次のルールを使用して、どのシンボルがパッケージの外部に表示されるかを判断できます。

  • 名前がアンダースコアで始まるシンボル(ただし、ダンダーネームは除く)は非公開と見なされます。

  • インポートされたシンボルはデフォルトで非公開と見なされます。 固定されたセットの import forms は、インポートされたシンボルを再エクスポートします。

  • モジュールは、モジュールレベルで __all__ シンボルを公開し、インターフェースの一部と見なされる名前のリストを提供できます。 これにより、上記の他のすべてのルールが上書きされ、インポートされたシンボルやアンダースコアで始まる名前のシンボルをインターフェースに含めることができます。

  • 関数内のローカル変数(ネストされた関数を含む)は常に非公開と見なされます。

次のイディオムは、__all__ に含まれる値を定義するためにサポートされています。 これらの制限により、型チェッカーは __all__ の値を静的に決定できます。

  • __all__ = ('a', b')

  • __all__ = ['a', b']

  • __all__ += ['a', b']

  • __all__ += submodule.__all__

  • __all__.extend(['a', b'])

  • __all__.extend(submodule.__all__)

  • __all__.append('a')

  • __all__.remove('a')

インポートの慣例

慣例により、特定のインポート形式は、インポートされたシンボルが再エクスポートされ、インポートモジュールの公開インターフェースの一部と見なされることを型チェッカーに示します。 他のすべてのインポートされたシンボルはデフォルトで非公開と見なされます。

次のインポート形式はシンボルを再エクスポートします:

  • import X as X``(冗長なモジュールエイリアス): ``X を再エクスポートします。

  • from Y import X as X``(冗長なシンボルエイリアス): ``X を再エクスポートします。

  • from Y import *: Y がモジュールレベルの __all__ リストを定義している場合、__all__ のすべての名前を再エクスポートします。 そうでない場合、Y のグローバルスコープ内のすべての公開シンボルを再エクスポートします。