# What is PEP 649?


[PEP 649: Deferred Evaluation of Annotations Using Descriptors](https://peps.python.org/pep-0649/) changes when Python evaluates type annotations. From Python 3.14, annotations are no longer computed at function or class definition time; they're computed lazily, on first access. Its companion [PEP 749: Implementing PEP 649](https://peps.python.org/pep-0749/) adds the `annotationlib` module for inspecting annotations under the new model.

Together they resolve a tension that goes back to [PEP 484](https://pydevtools.com/handbook/explanation/what-is-pep-484.md) itself: type annotations that reference names defined later in the file used to crash at import time, and the workaround (`from __future__ import annotations`) broke libraries that read annotations at runtime.

## What changed in Python 3.14?

Forward references now work without quotes or future imports:

```python
class Node:
    next: Node | None = None  # 3.14: fine; older versions: NameError at class creation
```

Under the old eager model, Python evaluated `Node | None` while still defining `Node`, so the name didn't exist yet. Under PEP 649, the interpreter compiles the annotation into a hidden `__annotate__` function and only calls it when something first asks for `Node.__annotations__`. A class whose annotations mention completely undefined names now defines without error; the evaluation (and any `NameError`) happens at inspection time instead of import time.

Code that never inspects annotations, which is most code, pays nothing for them.

## What does annotationlib provide?

Runtime tools ask for annotations in one of three formats (a fourth, `VALUE_WITH_FAKE_GLOBALS`, is reserved for tool internals):

- `VALUE` evaluates the annotations and returns real objects. This raises `NameError` if a referenced name doesn't exist yet.
- `FORWARDREF` returns real objects where possible and `ForwardRef` placeholders for names that don't resolve yet.
- `STRING` returns the annotation source text without evaluating anything.

```python
import annotationlib

class C:
    x: Undefined  # never defined anywhere

annotationlib.get_annotations(C, format=annotationlib.Format.FORWARDREF)
# {'x': ForwardRef('Undefined', is_class=True, owner=<class 'C'>)}

annotationlib.get_annotations(C, format=annotationlib.Format.STRING)
# {'x': 'Undefined'}
```

`inspect.signature()` gained a matching `annotation_format` parameter. `typing.get_type_hints()` keeps working as before; `annotationlib.get_annotations()` is the more capable replacement.

The `FORWARDREF` format is the headline for runtime tools. Validators like Pydantic and the stdlib's own `dataclasses` read annotations while building classes, often before every referenced name exists; `FORWARDREF` lets them collect what resolves and revisit the placeholders later instead of crashing.

## Do you still need `from __future__ import annotations`?

For code that runs only on 3.14+: no. The future import was the standard fix for forward references and for the import-time cost of evaluating annotations, and PEP 649 addresses both by default.

For code supporting older versions: keep it until your minimum is 3.14. The future import still works on 3.14 (it switches that module back to [PEP 563](https://peps.python.org/pep-0563/) string annotations), and CPython has committed to keeping it at least until Python 3.13 reaches end of life in October 2029 ([python/cpython#127639](https://github.com/python/cpython/issues/127639)).

Static type checkers like [mypy](https://pydevtools.com/handbook/reference/mypy.md) and [ty](https://pydevtools.com/handbook/reference/ty.md) never evaluate annotations, so they behave the same under all three regimes. The differences only bite tools that inspect annotations at runtime.

## Learn More

- [PEP 649: Deferred Evaluation of Annotations Using Descriptors](https://peps.python.org/pep-0649/)
- [PEP 749: Implementing PEP 649](https://peps.python.org/pep-0749/)
- [annotationlib documentation](https://docs.python.org/3/library/annotationlib.html)
- [What's New in Python 3.14: PEP 649](https://docs.python.org/3/whatsnew/3.14.html#whatsnew314-pep649)
- [What is PEP 604?](https://pydevtools.com/handbook/explanation/what-is-pep-604.md) covers a version trap that deferred evaluation interacts with
