Type checker directives¶
assert_type()¶
The functiontyping.assert_type(val,typ) allows users toask a static type checker to confirm that the inferred type ofvalisequivalent totyp.
When a type checker encounters a call toassert_type(), itshould emit an error if the value is not of the specified type:
defgreet(name:str)->None:assert_type(name,str)# OK, inferred type of `name` is `str`assert_type(name,int)# type checker error
If the two types areequivalent but syntactically different,the type checker may reject theassert_type() call:
fromtypingimportassert_type,Literaldefgreet(name:str)->None:assert_type(name,str|Literal["spam"])# type checker may error
Type checkers should aim to minimize cases where they rejectassert_type() calls that use equivalent types.
The second argument must be a validtype expression.
reveal_type()¶
The functionreveal_type(obj) makes type checkersreveal the inferred static type of an expression.
When a static type checker encounters a call to this function,it should emit a diagnostic with the type of the argument. For example:
x:int=1reveal_type(x)# Revealed type is "builtins.int"
#type:ignore comments¶
The special comment#type:ignore is used to silence type checkererrors.
The#type:ignore comment should be put on the line that theerror refers to:
importhttp.clienterrors={'not_found':http.client.NOT_FOUND# type: ignore}
A#type:ignore comment on a line by itself at the top of a file,before any docstrings, imports, or other executable code, silences allerrors in the file. Blank lines and other comments, such as shebanglines and coding cookies, may precede the#type:ignore comment.
In some cases, linting tools or other comments may be needed on the sameline as a type comment. In these cases, the type comment should be beforeother comments and linting markers:
# type: ignore # <comment or other marker>
cast()¶
Occasionally the type checker may need a different kind of hint: theprogrammer may know that an expression is of a more constrained typethan a type checker may be able to infer. For example:
fromtypingimportcastdeffind_first_str(a:list[object])->str:index=next(ifori,xinenumerate(a)ifisinstance(x,str))# We only get here if there's at least one string in areturncast(str,a[index])
Some type checkers may not be able to infer that the type ofa[index] isstr and only inferobject orAny, but weknow that (if the code gets to that point) it must be a string. Thecast(t,x) call tells the type checker that we are confident thatthe type ofx ist.t must be a validtype expression.At runtime a cast always returns theexpression unchanged – it does not check the type, and it does notconvert or coerce the value.
TYPE_CHECKING¶
Sometimes there’s code that must be seen by a type checker (or otherstatic analysis tools) but should not be executed. For suchsituations thetyping module defines a constant,TYPE_CHECKING, that is consideredTrue during type checking(or other static analysis) butFalse at runtime. Example:
importtypingiftyping.TYPE_CHECKING:importexpensive_moddefa_func(arg:'expensive_mod.SomeClass')->None:a_var:expensive_mod.SomeClass=arg...
(Note that the type annotation must be enclosed in quotes, making it a“forward reference”, to hide theexpensive_mod reference from theinterpreter runtime. In the variable annotation no quotes are needed.)
This approach may also be useful to handle import cycles.
@no_type_check¶
The@typing.no_type_check decorator may be supported by type checkersfor functions and classes.
If a type checker supports theno_type_check decorator for functions, itshould suppress all type errors for thedef statement and its body includingany nested functions or classes. It should also ignore all parameterand return type annotations and treat the function as if it were unannotated.
The behavior for theno_type_check decorator when applied to a class isleft undefined by the typing spec at this time.
Version and platform checking¶
Type checkers are expected to understand simple version and platformchecks, e.g.:
importsysifsys.version_info>=(3,12):# Python 3.12+else:# Python 3.11 and lowerifsys.platform=='win32':# Windows specific definitionselse:# Posix specific definitions
Don’t expect a checker to understand obfuscations like"".join(reversed(sys.platform))=="xunil".
@deprecated¶
(Originally specified inPEP 702.)
Thewarnings.deprecated()decorator can be used on a class, function or method to mark it as deprecated.This includestyping.TypedDict andtyping.NamedTuple definitions.With overloaded functions, the decorator may be applied to individual overloads,indicating that the particular overload is deprecated. The decorator may also beapplied to the overload implementation function, indicating that the entire functionis deprecated.
The decorator takes the following arguments:
A required positional-only argument representing the deprecation message.
Two keyword-only arguments,
categoryandstacklevel, controllingruntime behavior. These are not relevant to type checker behavior so they arenot further described in this specification.
The positional-only argument is of typestr and contains a message that shouldbe shown by the type checker when it encounters a usage of the decorated object.Tools may clean up the deprecation message for display, for exampleby usinginspect.cleandoc() or equivalent logic.The message must be a string literal.The content of deprecation messages is up to the user, but it may include the versionin which the deprecated object is to be removed, and information about suggestedreplacement APIs.
Type checkers should produce a diagnostic whenever they encounter a usage of anobject marked as deprecated. For deprecated overloads, this includes all callsthat resolve to the deprecated overload.For deprecated classes and functions, this includes:
References through module, class, or instance attributes (
module.deprecated_object,module.SomeClass.deprecated_method,module.SomeClass().deprecated_method)Any usage of deprecated objects in their defining module(
x=deprecated_object()inmodule.py)If
import*is used, usage of deprecated objects from themodule (frommoduleimport*;x=deprecated_object())fromimports (frommoduleimportdeprecated_object)Any syntax that indirectly triggers a call to the function. For example,if the
__add__method of a classCis deprecated, thenthe codeC()+C()should trigger a diagnostic. Similarly, if thesetter of a property is marked deprecated, attempts to set the propertyshould trigger a diagnostic.
If a method is marked with the@override decoratorand the base class method it overrides is deprecated, the type checker shouldproduce a diagnostic.
There are additional scenarios where deprecations could come into play.For example, an object may implement atyping.Protocol, but oneof the methods required for protocol compliance is deprecated.As scenarios such as this one appear complex and relatively unlikely to come up in practice,type checkers are not mandated to detect them.
Example¶
As an example, consider this library stub namedlibrary.pyi:
fromwarningsimportdeprecated@deprecated("Use Spam instead")classHam:...@deprecated("It is pining for the fiords")defnorwegian_blue(x:int)->int:...@overload@deprecated("Only str will be allowed")deffoo(x:int)->str:...@overloaddeffoo(x:str)->str:...classSpam:@deprecated("There is enough spam in the world")def__add__(self,other:object)->object:...@property@deprecated("All spam will be equally greasy")defgreasy(self)->float:...@propertydefshape(self)->str:...@shape.setter@deprecated("Shapes are becoming immutable")defshape(self,value:str)->None:...
Here is how type checkers should handle usage of this library:
fromlibraryimportHam# error: Use of deprecated class Ham. Use Spam instead.importlibrarylibrary.norwegian_blue(1)# error: Use of deprecated function norwegian_blue. It is pining for the fiords.map(library.norwegian_blue,[1,2,3])# error: Use of deprecated function norwegian_blue. It is pining for the fiords.library.foo(1)# error: Use of deprecated overload for foo. Only str will be allowed.library.foo("x")# no errorham=Ham()# no error (already reported above)spam=library.Spam()spam+1# error: Use of deprecated method Spam.__add__. There is enough spam in the world.spam.greasy# error: Use of deprecated property Spam.greasy. All spam will be equally greasy.spam.shape# no errorspam.shape="cube"# error: Use of deprecated property setter Spam.shape. Shapes are becoming immutable.
The exact wording of the diagnostics is up to the type checker and is not partof the specification.
Type checker behavior¶
It is unspecified exactly how type checkers should present deprecationdiagnostics to their users. However, some users (e.g., application developerstargeting only a specific version of Python) may not care about deprecations,while others (e.g., library developers who want their library to remaincompatible with future versions of Python) would want to catch any use ofdeprecated functionality in their CI pipeline. Therefore, it is recommendedthat type checkers provide configuration options that cover both use cases.As with any other type checker error, it is also possible to ignore deprecationsusing#type:ignore comments.