Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark, in light modeAuto light/dark, in dark modeSkip to content
mypy 1.16.1 documentation
Logo
mypy 1.16.1 documentation

First steps

Type system reference

Configuring and running mypy

Miscellaneous

Project Links

Back to top

Annotation issues at runtime

Idiomatic use of type annotations can sometimes run up against what a givenversion of Python considers legal code. This section describes these scenariosand explains how to get your code running again. Generally speaking, we havethree tools at our disposal:

  • Use of string literal types or type comments

  • Use oftyping.TYPE_CHECKING

  • Use offrom__future__importannotations (PEP 563)

We provide a description of these before moving onto discussion of specificproblems you may encounter.

String literal types and type comments

Mypy lets you add type annotations using the (now deprecated)#type:type comment syntax. These were required with Python versions older than 3.6,since they didn’t support type annotations on variables. Example:

a=1# type: intdeff(x):# type: (int) -> intreturnx+1# Alternative type comment syntax for functions with many argumentsdefsend_email(address,# type: Union[str, List[str]]sender,# type: strcc,# type: Optional[List[str]]subject='',body=None# type: List[str]):# type: (...) -> bool

Type comments can’t cause runtime errors because comments are not evaluated byPython.

In a similar way, using string literal types sidesteps the problem ofannotations that would cause runtime errors.

Any type can be entered as a string literal, and you can combinestring-literal types with non-string-literal types freely:

deff(a:list['A'])->None:...# OK, prevents NameError since A is defined laterdefg(n:'int')->None:...# Also OK, though not usefulclassA:pass

String literal types are never needed in#type: comments andstub files.

String literal types must be defined (or imported) laterin the same module.They cannot be used to leave cross-module references unresolved. (For dealingwith import cycles, seeImport cycles.)

Future annotations import (PEP 563)

Many of the issues described here are caused by Python trying to evaluateannotations. Future Python versions (potentially Python 3.14) will by default nolonger attempt to evaluate function and variable annotations. This behaviour ismade available in Python 3.7 and later through the use offrom__future__importannotations.

This can be thought of as automatic string literal-ification of all function andvariable annotations. Note that function and variable annotations are stillrequired to be valid Python syntax. For more details, seePEP 563.

Note

Even with the__future__ import, there are some scenarios that couldstill require string literals or result in errors, typically involving useof forward references or generics in:

# base class examplefrom__future__importannotationsclassA(tuple['B','C']):...# String literal types needed hereclassB:...classC:...

Warning

Some libraries may have use cases for dynamic evaluation of annotations, forinstance, through use oftyping.get_type_hints oreval. If yourannotation would raise an error when evaluated (say by usingPEP 604syntax with Python 3.9), you may need to be careful when using suchlibraries.

typing.TYPE_CHECKING

Thetyping module defines aTYPE_CHECKING constantthat isFalse at runtime but treated asTrue while type checking.

Since code insideifTYPE_CHECKING: is not executed at runtime, it providesa convenient way to tell mypy something without the code being evaluated atruntime. This is most useful for resolvingimport cycles.

Class name forward references

Python does not allow references to a class object before the class isdefined (aka forward reference). Thus this code does not work as expected:

deff(x:A)->None:...# NameError: name "A" is not definedclassA:...

Starting from Python 3.7, you can addfrom__future__importannotations toresolve this, as discussed earlier:

from__future__importannotationsdeff(x:A)->None:...# OKclassA:...

For Python 3.6 and below, you can enter the type as a string literal or type comment:

deff(x:'A')->None:...# OK# Also OKdefg(x):# type: (A) -> None...classA:...

Of course, instead of using future annotations import or string literal types,you could move the function definition after the class definition. This is notalways desirable or even possible, though.

Import cycles

An import cycle occurs where module A imports module B and module Bimports module A (perhaps indirectly, e.g.A->B->C->A).Sometimes in order to add type annotations you have to add extraimports to a module and those imports cause cycles that didn’t existbefore. This can lead to errors at runtime like:

ImportError: cannot import name 'b' from partially initialized module 'A' (most likely due to a circular import)

If those cycles do become a problem when running your program, there’s a trick:if the import is only needed for type annotations and you’re using a) thefuture annotations import, or b) string literals or typecomments for the relevant annotations, you can write the imports insideifTYPE_CHECKING: so that they are not executed at runtime. Example:

Filefoo.py:

fromtypingimportTYPE_CHECKINGifTYPE_CHECKING:importbardeflistify(arg:'bar.BarClass')->'list[bar.BarClass]':return[arg]

Filebar.py:

fromfooimportlistifyclassBarClass:deflistifyme(self)->'list[BarClass]':returnlistify(self)

Using classes that are generic in stubs but not at runtime

Some classes are declared asgeneric in stubs, but notat runtime.

In Python 3.8 and earlier, there are several examples within the standard library,for instance,os.PathLike andqueue.Queue. Subscriptingsuch a class will result in a runtime error:

fromqueueimportQueueclassTasks(Queue[str]):# TypeError: 'type' object is not subscriptable...results:Queue[int]=Queue()# TypeError: 'type' object is not subscriptable

To avoid errors from use of these generics in annotations, just use thefuture annotations import (or string literals or typecomments for Python 3.6 and below).

To avoid errors when inheriting from these classes, things are a little morecomplicated and you need to usetyping.TYPE_CHECKING:

fromtypingimportTYPE_CHECKINGfromqueueimportQueueifTYPE_CHECKING:BaseQueue=Queue[str]# this is only processed by mypyelse:BaseQueue=Queue# this is not seen by mypy but will be executed at runtimeclassTasks(BaseQueue):# OK...task_queue:Tasksreveal_type(task_queue.get())# Reveals str

If your subclass is also generic, you can use the following (using thelegacy syntax for generic classes):

fromtypingimportTYPE_CHECKING,TypeVar,GenericfromqueueimportQueue_T=TypeVar("_T")ifTYPE_CHECKING:class_MyQueueBase(Queue[_T]):passelse:class_MyQueueBase(Generic[_T],Queue):passclassMyQueue(_MyQueueBase[_T]):passtask_queue:MyQueue[str]reveal_type(task_queue.get())# Reveals str

In Python 3.9 and later, we can just inherit directly fromQueue[str] orQueue[T]since itsqueue.Queue implements__class_getitem__(), sothe class object can be subscripted at runtime. You may still encounter issues (even ifyou use a recent Python version) when subclassing generic classes defined in third-partylibraries if types are generic only in stubs.

Using types defined in stubs but not at runtime

Sometimes stubs that you’re using may define types you wish to reuse that donot exist at runtime. Importing these types naively will cause your code to failat runtime withImportError orModuleNotFoundError. Similar to previoussections, these can be dealt with by usingtyping.TYPE_CHECKING:

from__future__importannotationsfromtypingimportTYPE_CHECKINGifTYPE_CHECKING:from_typeshedimportSupportsRichComparisondeff(x:SupportsRichComparison)->None

Thefrom__future__importannotations is required to avoidaNameError when using the imported symbol.For more information and caveats, see the section onfuture annotations.

Using generic builtins

Starting with Python 3.9 (PEP 585), the type objects of many collections inthe standard library support subscription at runtime. This means that you nolonger have to import the equivalents fromtyping; you can simply usethe built-in collections or those fromcollections.abc:

fromcollections.abcimportSequencex:list[str]y:dict[int,str]z:Sequence[str]=x

There is limited support for using this syntax in Python 3.7 and later as well:if you usefrom__future__importannotations, mypy will understand thissyntax in annotations. However, since this will not be supported by the Pythoninterpreter at runtime, make sure you’re aware of the caveats mentioned in thenotes atfuture annotations import.

Using X | Y syntax for Unions

Starting with Python 3.10 (PEP 604), you can spell union types asx:int|str, instead ofx:typing.Union[int,str].

There is limited support for using this syntax in Python 3.7 and later as well:if you usefrom__future__importannotations, mypy will understand thissyntax in annotations, string literal types, type comments and stub files.However, since this will not be supported by the Python interpreter at runtime(if evaluated,int|str will raiseTypeError:unsupportedoperandtype(s)for|:'type'and'type'), make sure you’re aware of the caveats mentioned inthe notes atfuture annotations import.

Using new additions to the typing module

You may find yourself wanting to use features added to thetypingmodule in earlier versions of Python than the addition.

The easiest way to do this is to install and use thetyping_extensionspackage from PyPI for the relevant imports, for example:

fromtyping_extensionsimportTypeIs

If you don’t want to rely ontyping_extensions being installed on newerPythons, you could alternatively use:

importsysifsys.version_info>=(3,13):fromtypingimportTypeIselse:fromtyping_extensionsimportTypeIs

This plays nicely well with followingPEP 508 dependency specification:typing_extensions;python_version<"3.13"

On this page

[8]ページ先頭

©2009-2025 Movatter.jp