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:
- def foo(a):
- return '(' + a.split() + ')' # No error!
This gives no error even though a.split()
is “obviously” a list(the author probably meant a.strip()
). The error is reportedonce you add annotations:
- def foo(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 use Any
, but beware:
- One of the values involved has type ‘Any’. Extending the aboveexample, if we were to leave out the annotation for
a
, we’d getno error:
- def foo(a) -> str:
- return '(' + a.split() + ')' # No error!
The reason is that if the type of a
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 an Any
.
If you’re having trouble debugging such situations,reveal_type() might come in handy.
Note that sometimes library stubs have imprecise type information,e.g. the pow()
builtin returns Any
(see typeshed issue 285 for the reason).
- :py:meth:
__init__ <object.__init__>
method has no annotatedarguments or return type annotation.init
is considered fully-annotated if at least one argument is annotated,while mypy will infer the return type asNone
.The implication is that, for ainit
methodthat has no argument, you’ll have to explicitly annotate the return typeasNone
to type-check thisinit
method:
- def foo(s: str) -> str:
- return s
- class A():
- def __init__(self, value: str): # Return type inferred as None, considered as typed method
- self.value = value
- foo(1) # error: Argument 1 to "foo" has incompatible type "int"; expected "str"
- class B():
- def __init__(self): # No argument is annotated, considered as untyped method
- foo(1) # No error!
- class C():
- def __init__(self) -> None: # Must specify return type to type-check
- foo(1) # error: Argument 1 to "foo" has incompatible type "int"; expected "str"
Some imports may be silently ignored. Another source ofunexpected
Any
values are the—ignore-missing-imports
and—follow-imports=skip
flags. When you use—ignore-missing-imports
,any imported module that cannot be found is silently replaced withAny
. When using—follow-imports=skip
the same is true formodules for which a.py
file is found but that are not specifiedon the command line. (If a.pyi
stub is found it is alwaysprocessed normally, regardless of the value of—follow-imports
.) To help debug the former situation (nomodule found at all) leave out—ignore-missing-imports
; to getclarity about the latter use—follow-imports=error
. You canread up about these and other useful flags in The mypy command line.A function annotated as returning a non-optional type returns ‘None’and mypy doesn’t complain.
- def foo() -> str:
- return None # No error!
You may have disabled strict optional checking (seeDisabling strict optional checking for more).