Movatterモバイル変換


[0]ホーム

URL:


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

First steps

Type system reference

Configuring and running mypy

Miscellaneous

Project Links

Back to top

Common issues and solutions

This section has examples of cases when you need to update your codeto use static typing, and ideas for working around issues if mypydoesn’t work as expected. Statically typed code is often identical tonormal Python code (except for type annotations), but sometimes you needto do things slightly differently.

No errors reported for obviously wrong code

There are several common reasons why obviously wrong code is notflagged as an error.

The function containing the error is not annotated.

Functions thatdo not have any annotations (neither for any argument nor for thereturn type) are not type-checked, and even the most blatant typeerrors (e.g.2+'a') pass silently. The solution is to addannotations. Where that isn’t possible, functions without annotationscan be checked using--check-untyped-defs.

Example:

deffoo(a):return'('+a.split()+')'# No error!

This gives no error even thougha.split() is “obviously” a list(the author probably meanta.strip()). The error is reportedonce you add annotations:

deffoo(a:str)->str:return'('+a.split()+')'# error: Unsupported operand types for + ("str" and "list[str]")

If you don’t know what types to add, you can useAny, but beware:

One of the values involved has type ‘Any’.

Extending the aboveexample, if we were to leave out the annotation fora, we’d getno error:

deffoo(a)->str:return'('+a.split()+')'# No error!

The reason is that if the type ofa is unknown, the type ofa.split() is also unknown, so it is inferred as having typeAny, and it is no error to add a string to anAny.

If you’re having trouble debugging such situations,reveal_type() might come in handy.

Note that sometimes library stubs with imprecise type informationcan be a source ofAny values.

__init__method has no annotatedarguments and no return type annotation.

This is basically a combination of the two cases above, in that__init__without annotations can causeAny types leak into instance variables:

classBad:def__init__(self):self.value="asdf"1+"asdf"# No error!bad=Bad()bad.value+1# No error!reveal_type(bad)# Revealed type is "__main__.Bad"reveal_type(bad.value)# Revealed type is "Any"classGood:def__init__(self)->None:# Explicitly return Noneself.value=value

Some imports may be silently ignored.

A common source of unexpectedAny values is the--ignore-missing-imports flag.

When you use--ignore-missing-imports,any imported module that cannot be found is silently replaced withAny.

To help debug this, simply leave out--ignore-missing-imports.As mentioned inMissing imports, settingignore_missing_imports=Trueon a per-module basis will make bad surprises less likely and is highly encouraged.

Use of the--follow-imports=skip flags can alsocause problems. Use of these flags is strongly discouraged and only required inrelatively niche situations. SeeFollowing imports for more information.

mypy considers some of your code unreachable.

SeeUnreachable code for more information.

A function annotated as returning a non-optional type returns ‘None’and mypy doesn’t complain.

deffoo()->str:returnNone# No error!

You may have disabled strict optional checking (see–no-strict-optional for more).

Spurious errors and locally silencing the checker

You can use a#type:ignore comment to silence the type checkeron a particular line. For example, let’s say our code is usingthe C extension modulefrobnicate, and there’s no stub available.Mypy will complain about this, as it has no information about themodule:

importfrobnicate# Error: No module "frobnicate"frobnicate.start()

You can add a#type:ignore comment to tell mypy to ignore thiserror:

importfrobnicate# type: ignorefrobnicate.start()# Okay!

The second line is now fine, since the ignore comment causes the namefrobnicate to get an implicitAny type.

Note

You can use the form#type:ignore[<code>] to only ignorespecific errors on the line. This way you are less likely tosilence unexpected errors that are not safe to ignore, and thiswill also document what the purpose of the comment is. SeeError codes for more information.

Note

The#type:ignore comment will only assign the implicitAnytype if mypy cannot find information about that particular module. So,if we did have a stub available forfrobnicate then mypy wouldignore the#type:ignore comment and typecheck the stub as usual.

Another option is to explicitly annotate values with typeAny –mypy will let you perform arbitrary operations onAnyvalues. Sometimes there is no more precise type you can use for aparticular value, especially if you use dynamic Python featuressuch as__getattr__:

classWrapper:...def__getattr__(self,a:str)->Any:returngetattr(self._wrapped,a)

Finally, you can create a stub file (.pyi) for a file thatgenerates spurious errors. Mypy will only look at the stub fileand ignore the implementation, since stub files take precedenceover.py files.

Ignoring a whole file

  • To only ignore errors, use a top-level#mypy:ignore-errors comment instead.

  • To only ignore errors with a specific error code, use a top-level#mypy:disable-error-code="..." comment. Example:#mypy:disable-error-code="truthy-bool,ignore-without-code"

  • To replace the contents of a module withAny, use a per-modulefollow_imports=skip.SeeFollowing imports for details.

Note that a#type:ignore comment at the top of a module (before any statements,including imports or docstrings) has the effect of ignoring the entire contents of the module.This behaviour can be surprising and result in“Module … has no attribute … [attr-defined]” errors.

Issues with code at runtime

Idiomatic use of type annotations can sometimes run up against what a givenversion of Python considers legal code. These can result in some of thefollowing errors when trying to run your code:

  • ImportError from circular imports

  • NameError:name"X"isnotdefined from forward references

  • TypeError:'type'objectisnotsubscriptable from types that are not generic at runtime

  • ImportError orModuleNotFoundError from use of stub definitions not available at runtime

  • TypeError:unsupportedoperandtype(s)for|:'type'and'type' from use of new syntax

For dealing with these, seeAnnotation issues at runtime.

Mypy runs are slow

If your mypy runs feel slow, you should probably use themypydaemon, which can speed up incremental mypy runtimes bya factor of 10 or more.Remote caching canmake cold mypy runs several times faster.

Furthermore: as ofmypy 1.13,mypy allows use of the orjson library for handling the cache instead of the stdlib json, forimproved performance. You can ensure the presence of orjson using the faster-cache extra:

python3 -m pip install -U mypy[faster-cache]

Mypy may depend on orjson by default in the future.

Types of empty collections

You often need to specify the type when you assign an empty list ordict to a new variable, as mentioned earlier:

a:list[int]=[]

Without the annotation mypy can’t always figure out theprecise type ofa.

You can use a simple empty list literal in a dynamically typed function (as thetype ofa would be implicitlyAny and need not be inferred), if typeof the variable has been declared or inferred before, or if you perform a simplemodification operation in the same scope (such asappend for a list):

a=[]# Okay because followed by append, inferred type list[int]foriinrange(n):a.append(i*i)

However, in more complex cases an explicit type annotation can berequired (mypy will tell you this). Often the annotation canmake your code easier to understand, so it doesn’t only help mypy buteverybody who is reading the code!

Redefinitions with incompatible types

Each name within a function only has a single ‘declared’ type. You canreuse for loop indices etc., but if you want to use a variable withmultiple types within a single function, you may need to instead usemultiple variables (or maybe declare the variable with anAny type).

deff()->None:n=1...n='x'# error: Incompatible types in assignment (expression has type "str", variable has type "int")

Note

Using the--allow-redefinitionflag can suppress this error in several cases.

Note that you can redefine a variable with a moreprecise or a moreconcrete type. For example, you can redefine a sequence (which doesnot supportsort()) as a list and sort it in-place:

deff(x:Sequence[int])->None:# Type of x is Sequence[int] here; we don't know the concrete type.x=list(x)# Type of x is list[int] here.x.sort()# Okay!

SeeType narrowing for more information.

Invariance vs covariance

Most mutable generic collections are invariant, and mypy considers alluser-defined generic classes invariant by default(seeVariance of generic types for motivation). This could lead to someunexpected errors when combined with type inference. For example:

classA:...classB(A):...lst=[A(),A()]# Inferred type is list[A]new_lst=[B(),B()]# inferred type is list[B]lst=new_lst# mypy will complain about this, because List is invariant

Possible strategies in such situations are:

  • Use an explicit type annotation:

    new_lst:list[A]=[B(),B()]lst=new_lst# OK
  • Make a copy of the right hand side:

    lst=list(new_lst)# Also OK
  • Use immutable collections as annotations whenever possible:

    deff_bad(x:list[A])->A:returnx[0]f_bad(new_lst)# Failsdeff_good(x:Sequence[A])->A:returnx[0]f_good(new_lst)# OK

Declaring a supertype as variable type

Sometimes the inferred type is a subtype (subclass) of the desiredtype. The type inference uses the first assignment to infer the typeof a name:

classShape:...classCircle(Shape):...classTriangle(Shape):...shape=Circle()# mypy infers the type of shape to be Circleshape=Triangle()# error: Incompatible types in assignment (expression has type "Triangle", variable has type "Circle")

You can just give an explicit type for the variable in cases such theabove example:

shape:Shape=Circle()# The variable s can be any Shape, not just Circleshape=Triangle()# OK

Complex type tests

Mypy can usually infer the types correctly when usingisinstance,issubclass,ortype(obj)issome_class type tests,and evenuser-defined type guards,but for other kinds of checks you may need to add anexplicit type cast:

fromcollections.abcimportSequencefromtypingimportcastdeffind_first_str(a:Sequence[object])->str:index=next((ifori,sinenumerate(a)ifisinstance(s,str)),-1)ifindex<0:raiseValueError('No str found')found=a[index]# Has type "object", despite the fact that we know it is "str"returncast(str,found)# We need an explicit cast to make mypy happy

Alternatively, you can use anassert statement together with someof the supported type inference techniques:

deffind_first_str(a:Sequence[object])->str:index=next((ifori,sinenumerate(a)ifisinstance(s,str)),-1)ifindex<0:raiseValueError('No str found')found=a[index]# Has type "object", despite the fact that we know it is "str"assertisinstance(found,str)# Now, "found" will be narrowed to "str"returnfound# No need for the explicit "cast()" anymore

Note

Note that theobject type used in the above example is similartoObject in Java: it only supports operations defined forallobjects, such as equality andisinstance(). The typeAny,in contrast, supports all operations, even if they may fail atruntime. The cast above would have been unnecessary if the type ofo wasAny.

Note

You can read more about type narrowing techniqueshere.

Type inference in Mypy is designed to work well in common cases, to bepredictable and to let the type checker give useful errormessages. More powerful type inference strategies often have complexand difficult-to-predict failure modes and could result in veryconfusing error messages. The tradeoff is that you as a programmersometimes have to give the type checker a little help.

Python version and system platform checks

Mypy supports the ability to perform Python version checks and platformchecks (e.g. Windows vs Posix), ignoring code paths that won’t be run onthe targeted Python version or platform. This allows you to more effectivelytypecheck code that supports multiple versions of Python or multiple operatingsystems.

More specifically, mypy will understand the use ofsys.version_info andsys.platform checks withinif/elif/else statements. For example:

importsys# Distinguishing between different versions of Python:ifsys.version_info>=(3,13):# Python 3.13+ specific definitions and importselse:# Other definitions and imports# Distinguishing between different operating systems:ifsys.platform.startswith("linux"):# Linux-specific codeelifsys.platform=="darwin":# Mac-specific codeelifsys.platform=="win32":# Windows-specific codeelse:# Other systems

As a special case, you can also use one of these checks in a top-level(unindented)assert; this makes mypy skip the rest of the file.Example:

importsysassertsys.platform!='win32'# The rest of this file doesn't apply to Windows.

Some other expressions exhibit similar behavior; in particular,TYPE_CHECKING, variables namedMYPY orTYPE_CHECKING, and any variablewhose name is passed to--always-true or--always-false.(However,True andFalse are not treated specially!)

Note

Mypy currently does not support more complex checks, and does not assignany special meaning when assigning asys.version_info orsys.platformcheck to a variable. This may change in future versions of mypy.

By default, mypy will use your current version of Python and your currentoperating system as default values forsys.version_info andsys.platform.

To target a different Python version, use the--python-versionX.Y flag.For example, to verify your code typechecks if were run using Python 3.8, passin--python-version3.8 from the command line. Note that you do not needto have Python 3.8 installed to perform this check.

To target a different operating system, use the--platformPLATFORM flag.For example, to verify your code typechecks if it were run in Windows, passin--platformwin32. See the documentation forsys.platformfor examples of valid platform parameters.

Displaying the type of an expression

You can usereveal_type(expr) to ask mypy to display the inferredstatic type of an expression. This can be useful when you don’t quiteunderstand how mypy handles a particular piece of code. Example:

reveal_type((1,'hello'))# Revealed type is "tuple[builtins.int, builtins.str]"

You can also usereveal_locals() at any line in a fileto see the types of all local variables at once. Example:

a=1b='one'reveal_locals()# Revealed local types are:#     a: builtins.int#     b: builtins.str

Note

reveal_type andreveal_locals are handled specially by mypy duringtype checking, and don’t have to be defined or imported.

However, if you want to run your code,you’ll have to remove anyreveal_type andreveal_localscalls from your program or else Python will give you an error at runtime.

Alternatively, you can importreveal_type fromtyping_extensionsortyping (on Python 3.11 and newer)

Silencing linters

In some cases, linters will complain about unused imports or code. Inthese cases, you can silence them with a comment after type comments, or onthe same line as the import:

# to silence complaints about unused importsfromtypingimportList# noqaa=None# type: List[int]

To silence the linter on the same line as a type commentput the linter commentafter the type comment:

a=some_complex_thing()# type: ignore  # noqa

Covariant subtyping of mutable protocol members is rejected

Mypy rejects this because this is potentially unsafe.Consider this example:

fromtypingimportProtocolclassP(Protocol):x:floatdeffun(arg:P)->None:arg.x=3.14classC:x=42c=C()fun(c)# This is not safec.x<<5# Since this will fail!

To work around this problem consider whether “mutating” is actually partof a protocol. If not, then one can use a@property inthe protocol definition:

fromtypingimportProtocolclassP(Protocol):@propertydefx(self)->float:passdeffun(arg:P)->None:...classC:x=42fun(C())# OK

Dealing with conflicting names

Suppose you have a class with a method whose name is the same as animported (or built-in) type, and you want to use the type in anothermethod signature. E.g.:

classMessage:defbytes(self):...defregister(self,path:bytes):# error: Invalid type "mod.Message.bytes"...

The third line elicits an error because mypy sees the argument typebytes as a reference to the method by that name. Other thanrenaming the method, a workaround is to use an alias:

bytes_=bytesclassMessage:defbytes(self):...defregister(self,path:bytes_):...

Using a development mypy build

You can install the latest development version of mypy from source. Clone themypy repository on GitHub, and then runpipinstall locally:

git clone https://github.com/python/mypy.gitcd mypypython3 -m pip install --upgrade .

To install a development version of mypy that is mypyc-compiled, see theinstructions at themypyc wheels repo.

Variables vs type aliases

Mypy has bothtype aliases and variables with types liketype[...]. These aresubtly different, and it’s important to understand how they differ to avoid pitfalls.

  1. A variable with typetype[...] is defined using an assignment with anexplicit type annotation:

    classA:...tp:type[A]=A
  2. You can define a type alias using an assignment without an explicit type annotationat the top level of a module:

    classA:...Alias=A

    You can also useTypeAlias (PEP 613) to define anexplicit type alias:

    fromtypingimportTypeAlias# "from typing_extensions" in Python 3.9 and earlierclassA:...Alias:TypeAlias=A

    You should always useTypeAlias to define a type alias in a class body orinside a function.

The main difference is that the target of an alias is precisely known statically, and thismeans that they can be used in type annotations and othertype contexts. Type aliasescan’t be defined conditionally (unless usingsupported Python version and platform checks):

classA:...classB:...ifrandom()>0.5:Alias=Aelse:# error: Cannot assign multiple types to name "Alias" without an# explicit "Type[...]" annotationAlias=Btp:type[object]# "tp" is a variable with a type object valueifrandom()>0.5:tp=Aelse:tp=B# This is OKdeffun1(x:Alias)->None:...# OKdeffun2(x:tp)->None:...# Error: "tp" is not valid as a type

Incompatible overrides

It’s unsafe to override a method with a more specific argument type,as it violates theLiskov substitution principle.For return types, it’s unsafe to override a method with a more generalreturn type.

Other incompatible signature changes in method overrides, such asadding an extra required parameter, or removing an optional parameter,will also generate errors. The signature of a method in a subclassshould accept all valid calls to the base class method. Mypytreats a subclass as a subtype of the base class. An instance of asubclass is valid everywhere where an instance of the base class isvalid.

This example demonstrates both safe and unsafe overrides:

fromcollections.abcimportSequence,IterableclassA:deftest(self,t:Sequence[int])->Sequence[str]:...classGeneralizedArgument(A):# A more general argument type is okaydeftest(self,t:Iterable[int])->Sequence[str]:# OK...classNarrowerArgument(A):# A more specific argument type isn't accepteddeftest(self,t:list[int])->Sequence[str]:# Error...classNarrowerReturn(A):# A more specific return type is finedeftest(self,t:Sequence[int])->list[str]:# OK...classGeneralizedReturn(A):# A more general return type is an errordeftest(self,t:Sequence[int])->Iterable[str]:# Error...

You can use#type:ignore[override] to silence the error. Add itto the line that generates the error, if you decide that type safety isnot necessary:

classNarrowerArgument(A):deftest(self,t:list[int])->Sequence[str]:# type: ignore[override]...

Unreachable code

Mypy may consider some code asunreachable, even if it might not beimmediately obvious why. It’s important to note that mypy willnottype check such code. Consider this example:

classFoo:bar:str=''defbar()->None:foo:Foo=Foo()returnx:int='abc'# Unreachable -- no error

It’s easy to see that any statement afterreturn is unreachable,and hence mypy will not complain about the mistyped code belowit. For a more subtle example, consider this code:

classFoo:bar:str=''defbar()->None:foo:Foo=Foo()assertfoo.barisNonex:int='abc'# Unreachable -- no error

Again, mypy will not report any errors. The type offoo.bar isstr, and mypy reasons that it can never beNone. Hence theassert statement will always fail and the statement below willnever be executed. (Note that in Python,None is not an emptyreference but an object of typeNone.)

In this example mypy will go on to check the last line and report anerror, since mypy thinks that the condition could be either True orFalse:

classFoo:bar:str=''defbar()->None:foo:Foo=Foo()ifnotfoo.bar:returnx:int='abc'# Reachable -- error

If you use the--warn-unreachable flag, mypy will generatean error about each unreachable code block.

Narrowing and inner functions

Because closures in Python are late-binding (https://docs.python-guide.org/writing/gotchas/#late-binding-closures),mypy will not narrow the type of a captured variable in an inner function.This is best understood via an example:

deffoo(x:int|None)->Callable[[],int]:ifxisNone:x=5print(x+1)# mypy correctly deduces x must be an int heredefinner()->int:returnx+1# but (correctly) complains about this linex=None# because x could later be assigned Nonereturninnerinner=foo(5)inner()# this will raise an error when called

To get this code to type check, you could assigny=x afterx has beennarrowed, and usey in the inner function, or add an assert in the innerfunction.

Incorrect use ofSelf

Self is not the type of the current class; it’s a type variable with upperbound of the current class. That is, it represents the type of the current classor of potential subclasses.

fromtypingimportSelfclassFoo:@classmethoddefconstructor(cls)->Self:# Instead, either call cls() or change the annotation to -> FooreturnFoo()# error: Incompatible return value type (got "Foo", expected "Self")classBar(Foo):...reveal_type(Foo.constructor())# note: Revealed type is "Foo"# In the context of the subclass Bar, the Self return type promises# that the return value will be Barreveal_type(Bar.constructor())# note: Revealed type is "Bar"
On this page

[8]ページ先頭

©2009-2025 Movatter.jp