Skip to content
Merged
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ peps/pep-0777.rst @warsaw
# ...
peps/pep-0779.rst @Yhg1s @colesbury @mpage
# ...
peps/pep-0781.rst @methane
# ...
peps/pep-0789.rst @njsmith
# ...
peps/pep-0801.rst @warsaw
Expand Down
122 changes: 122 additions & 0 deletions peps/pep-0781.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
PEP: 781
Title: Make ``TYPE_CHECKING`` a built-in constant
Author: Inada Naoki <songofacandy@gmail.com>
Discussions-To: https://discuss.python.org/t/85728
Status: Draft
Type: Standards Track
Topic: Typing
Created: 24-Mar-2025
Python-Version: 3.14
Post-History: `11-Jan-2025 <https://discuss.python.org/t/76766>`__,
`24-Mar-2025 <https://discuss.python.org/t/85728>`__,


Abstract
========

This PEP proposes adding a new built-in variable, :data:`!TYPE_CHECKING`, to
improve the experience of writing Python code with type annotations. It is
evaluated as ``True`` when the code is being analyzed by a static type checker,
and as ``False`` during normal runtime execution. Unlike
:data:`typing.TYPE_CHECKING`, which this variable replaces, it does not require
an import statement.


Motivation
==========

Type annotations were defined for Python by :pep:`484`, and have enjoyed
widespread adoption. A challenge with fully-annotated code is that many
more imports are required in order to bring the relevant name into scope,
potentially causing import cycles without careful design. This has been
recognized by :pep:`563` and later :pep:`649`, which introduce two different
mechanisms for deferred evaluation of type annotations. As PEP 563 notes,
"type hints are ... not computationally free". The :data:`typing.TYPE_CHECKING`
constant was thus introduced__, initially to aid in breaking cyclic imports.

__ https://github.com/python/typing/issues/230

In situations where startup time is critical, such as command-line interfaces,
applications, or core libraries, programmers may place all import statements
not required for runtime execution within a 'TYPE_CHECKING block', or even
defer certain imports to within functions. The ``typing`` module itself though
can take as much as 10ms to import, longer than Python takes to initialize.
The time taken to import the ``typing`` module clearly cannot be ignored.

To avoid importing ``TYPE_CHECKING`` from ``typing``, developers currently
define a module-level variable such as ``TYPE_CHECKING = False`` or use code
like ``if False: # TYPE_CHECKING``. Providing a standard method will allow
many tools to implement the same behavior consistently. It will also allow
third-party tools in the ecosystem to standardize on a single behavior
with guaranteed semantics, as for example some static type checkers currently
do not permit local constants, only recognizing ``typing.TYPE_CHECKING``.


Specification
=============

``TYPE_CHECKING`` is a built-in constant and its value is ``False``.
Unlike ``True``, ``False``, ``None``, and ``__debug__``, ``TYPE_CHECKING`` is
not a real constant; assigning to it will not raise a ``SyntaxError``.

Static type checkers must treat ``TYPE_CHECKING`` as ``True``, similar to
:data:`typing.TYPE_CHECKING`.

If this PEP is accepted, the new ``TYPE_CHECKING`` constant will be
the preferred approach, instead of the existing ``typing.TYPE_CHECKING``.
However, ``typing.TYPE_CHECKING`` will not be deprecated in the foreseeable
future.


Backwards Compatibility
=======================

Since ``TYPE_CHECKING`` doesn't prohibit assignment, existing code using
``TYPE_CHECKING`` will continue to work.

.. code-block:: python

# This code will continue to work
TYPE_CHECKING = False
from typing import TYPE_CHECKING


User can remove the assignment to ``TYPE_CHECKING`` after they stop using
Python 3.13 or older versions.


How to Teach This
=================

* Use ``if TYPE_CHECKING:`` for skipping type-checking code at runtime.
* Use ``from typing import TYPE_CHECKING`` to support Python versions before
3.14.
* Workarounds like ``TYPE_CHECKING = False`` or ``if False: # TYPE_CHECKING``
will continue to work, but are not recommended.


Reference Implementation
========================

* `python/cpython#131793 <https://github.com/python/cpython/pull/131793>`__


Rejected Ideas
==============

Eliminate type-checking-only code
---------------------------------

It is considered to add real constant named ``__type_checking__``
to eliminate type-checking-only code at compile time.

However, adding real constant to language increase complexity of the language.
Benefit from eliminating type-checking-only code is estimated to be not enough
to justify the complexity.


Copyright
=========

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.