Python 3.14で__annotations__がNameErrorになる?annotationlibのSTRING・FORWARDREFで確認する方法

Python3.14のannotationlibでNameErrorを切り分ける図解。get_annotationsの既定VALUEは未定義名で失敗し得るため、表示用STRING、未解決参照用FORWARDREF、実型用VALUEを目的で選ぶ。

Python 3.14ではアノテーションが原則としてアクセス時に遅延評価されるため、関数定義が成功しても、未定義の型名をFormat.VALUEで読み取る段階でNameErrorになることがあります。表示や記録だけならFormat.STRING、未解決参照を保持して扱える解析処理ならFormat.FORWARDREF、実際の型オブジェクトが必要なら名前の定義・import完了後にFormat.VALUEを使います。

annotationlib.get_annotations()を使うだけでは解決しません。既定のformatはFormat.VALUEなので、用途に合う形式を明示する必要があります。Python 3.14のannotationlib公式資料では、VALUE、FORWARDREF、STRINGの3形式と、未定義名がある場合の違いが説明されています。

以下は2026年10月4日時点のPython 3.14公式資料に基づきます。

目次

Python 3.13以前・3.14・future importの違い

コードの状態注釈の基本動作未定義名が表面化する時点
Python 3.13以前の通常の注釈原則として定義時に評価関数やクラスの定義時
Python 3.14の通常の注釈必要になるまで遅延評価__annotations__やVALUEで読む時
Python 3.14でfrom __future__ import annotationsを使用従来どおり文字列化文字列を実値へ解決する処理の時

Python 3.14ではPEP 649・PEP 749による遅延評価が導入済みです。What’s New in Python 3.14によると、実行時に注釈を読むコードは取得形式の見直しが必要な場合があります。future importは3.14でも文字列化動作を維持しています。

最小例:定義は成功し、VALUEで読むと失敗する

次のコードはPython 3.14で、future importを使わない例です。Missingの定義前でもparse自体は定義できますが、注釈を値として読むと未定義名が評価されます。

from annotationlib import Format, ForwardRef, get_annotations


def parse(x: Missing) -> None:
    pass


try:
    print(parse.__annotations__)
except NameError as exc:
    print(type(exc).__name__)

print(get_annotations(parse, format=Format.STRING))

refs = get_annotations(parse, format=Format.FORWARDREF)
print(isinstance(refs['x'], ForwardRef))


class Missing:
    pass


annotations = get_annotations(parse, format=Format.VALUE)
print(annotations['x'] is Missing)

Format.STRINGはソースに近い文字列を返し、Format.FORWARDREFは解決できる値を実値、未定義名をForwardRefとして返します。この例では、型の定義後にVALUEで読み直せば、Missingの実クラスを取得できます。FORWARDREFの結果には、list[Missing]のような型の内部に参照が残る場合もあるため、消費側がその構造を扱えることも確認します。文字列は空白の正規化などで変わる可能性があり、厳密なソース復元用途ではありません。

注釈の用途別分岐図。表示にはSTRING、未解決名を扱うにはFORWARDREF、実型が必要なら定義やimport後にVALUEを選ぶ。どの形式もサンドボックスではなくコード実行や例外があり得る。
表示と実型取得を区別し、消費側の対応と信頼性を確認します。

実プロジェクトでの切り分け

  1. 実行版を確認する:python --versionとpython -c "import sys; print(sys.executable)"で、想定したPython 3.14を使っているか確認します。
  2. future importを確認する:対象モジュール先頭のfrom __future__ import annotationsの有無を記録します。3.14の通常動作と文字列化モードを混同しないでください。
  3. 失敗時点を分ける:定義時か、__annotations__、get_annotations()、inspect.signature()などを呼んだ時かを確認します。
  4. 未定義名の出所を探す:後で定義されるクラスか、循環importか、if TYPE_CHECKING:内だけでimportしている型かを確認します。TYPE_CHECKING内の名前は通常の実行時には定義されません。
  5. 消費側が必要とする形式を決める:ドキュメント表示ならSTRING、未解決参照を認識できる解析ならFORWARDREF、DIや実行時検証など実型が必要ならimport完了後のVALUEを選びます。
  6. 第三者ライブラリなら対応版を確認する:内部属性を書き換えず、公式のPython 3.14対応状況を確認します。

Python公式のアノテーションのベストプラクティスでは、3.14以降は直接属性を読むよりannotationlib.get_annotations()を使うことが推奨されています。直接代入や削除でエラーを隠す方法は避けてください。

inspect.signatureを表示目的で使う

シグネチャの表示だけが目的なら、Python 3.14で追加されたannotation_formatを指定できます。

import inspect
from annotationlib import Format


def parse(x: Missing) -> None:
    pass


signature = inspect.signature(
    parse,
    annotation_format=Format.STRING,
)
print(signature)

inspect.signature公式資料では、annotation_format=Format.STRINGで文字列形式を選べるとされています。この引数をPython 3.13向けコードへそのまま持ち込まないでください。

STRINGとFORWARDREFも安全なサンドボックスではない

公式のセキュリティ説明にあるとおり、STRINGやFORWARDREFを選んでも、任意の注釈が絶対に評価されず、例外も起きないとは限りません。カスタムのannotate関数や注釈式によって、例外やコード実行が発生する可能性があります。未信頼コードの注釈を安全に解析する仕組みとして扱わないでください。

よくある質問

get_annotations()へ変えればNameErrorは消えますか?

既定形式がVALUEなので、未定義名があれば同様に失敗します。目的に応じてformatを明示します。

eval_str=Falseなら遅延注釈を評価しませんか?

万能な停止指定ではありません。eval_strは主にfuture importなどで文字列化された注釈を再評価するかに関わり、通常の3.14遅延注釈をVALUEで読む動作とは分けて考えます。future importで文字列化された注釈は、既定のVALUEでもそのまま文字列を返します。実型へ解決したい場合は、必要な名前を定義した後でget_annotations(obj, eval_str=True, format=Format.VALUE)のように文字列の評価を明示します。eval_str=TrueはVALUE以外との組み合わせではエラーになります。

まとめ

Python 3.14のNameErrorは、遅延評価が壊れたのではなく、未定義名をVALUEとして読む時点へエラーが移った場合があります。まず注釈を読む目的を確認し、STRING、FORWARDREF、VALUEを選び分けてください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次