Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ context results in a type check error:
def p() -> None:
print('hello')

a = p() # Type check error: p has None return value
a = p() # error: "p" does not return a value

Arguments with default values can be annotated as follows:

Expand All @@ -63,7 +63,8 @@ more stable.
1 + 'x' # No static type error (dynamically typed)

def g() -> None:
1 + 'x' # Type check error (statically typed)
1 + 'x' # Statically typed, resulting in
# error: Unsupported operand types for + ("int" and "str")

.. note::

Expand Down
2 changes: 1 addition & 1 deletion docs/source/casts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ any operations on the result. For example:
from typing import cast, Any

x = 1
x + 'x' # Type check error
x + 'x' # error: Unsupported operand types for + ("int" and "str")
y = cast(Any, x)
y + 'x' # Type check OK (runtime error)
10 changes: 5 additions & 5 deletions docs/source/cheat_sheet.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Functions
# type: (int) -> None
pass
quux(3) # Fine
quux(__x=3) # Error
quux(__x=3) # error: Unexpected keyword argument "__x" for "quux"

# This is how you annotate a function value.
x = f # type: Callable[[int, float], float]
Expand All @@ -106,7 +106,7 @@ Functions
body=None # type: List[str]
):
# type: (...) -> bool
<code>
pass
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to keep <code> here because if you put pass it technically doesn't typecheck. Or, perhaps, we can make the return type of the function None instead of bool.

Copy link
Collaborator

@ilinum ilinum Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, just tested it and pass does typecheck :)
I think it's okay to leave pass there then.



When you're puzzled or when things are complicated
Expand All @@ -119,7 +119,7 @@ When you're puzzled or when things are complicated
# To find out what type mypy infers for an expression anywhere in
# your program, wrap it in reveal_type. Mypy will print an error
# message with the type; remove it again before running the code.
reveal_type(1) # -> error: Revealed type is 'builtins.int'
reveal_type(1) # error: Revealed type is 'builtins.int'

# Use Union when something could be one of a few types.
x = [3, 5, "test", "fun"] # type: List[Union[int, str]]
Expand All @@ -146,8 +146,8 @@ When you're puzzled or when things are complicated
a = [4]
b = cast(List[int], a) # passes fine
c = cast(List[str], a) # passes fine (no runtime check)
reveal_type(c) # -> error: Revealed type is 'builtins.list[builtins.str]'
print(c) # -> [4] the object is not cast
reveal_type(c) # error: Revealed type is 'builtins.list[builtins.str]'
print(c) # [4] the object is not cast
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, it would be good to give an indication that [4] is the output of the print statement.
Something like

print(c)  # prints [4] because the cast did not change the object's type at runtime


# TODO: explain "Need type annotation for variable" when
# initializing with None or an empty container
Expand Down
14 changes: 6 additions & 8 deletions docs/source/cheat_sheet_py3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Python 3 introduces an annotation syntax for function declarations in `PEP 3107
def quux(__x: int) -> None:
pass
quux(3) # Fine
quux(__x=3) # Error
quux(__x=3) # error: Unexpected keyword argument "__x" for "quux"

# This is how you annotate a function value.
x = f # type: Callable[[int, float], float]
Expand Down Expand Up @@ -113,7 +113,7 @@ When you're puzzled or when things are complicated
# To find out what type mypy infers for an expression anywhere in
# your program, wrap it in reveal_type. Mypy will print an error
# message with the type; remove it again before running the code.
reveal_type(1) # -> error: Revealed type is 'builtins.int'
reveal_type(1) # error: Revealed type is 'builtins.int'

# Use Union when something could be one of a few types.
x = [3, 5, "test", "fun"] # type: List[Union[int, str]]
Expand All @@ -139,8 +139,8 @@ When you're puzzled or when things are complicated
a = [4]
b = cast(List[int], a) # passes fine
c = cast(List[str], a) # passes fine (no runtime check)
reveal_type(c) # -> error: Revealed type is 'builtins.list[builtins.str]'
print(c) # -> [4] the object is not cast
reveal_type(c) # error: Revealed type is 'builtins.list[builtins.str]'
print(c) # [4] the object is not cast
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment for py2 cheatsheet.


# TODO: explain "Need type annotation for variable" when
# initializing with None or an empty container
Expand Down Expand Up @@ -190,8 +190,6 @@ Classes
def my_method(self, num: int, str1: str) -> str:
return num * str1



# User-defined classes are written with just their own names.
x = MyClass() # type: MyClass

Expand Down Expand Up @@ -228,7 +226,7 @@ Other stuff

# forward references are useful if you want to referemce a class before it is designed

def f(foo: A) -> int: # this will fail
def f(foo: A) -> int: # this will fail at runtime
...

class A:
Expand All @@ -253,7 +251,7 @@ Mypy brings limited support for PEP 526 annotations.
name: str = "Eric Idle"

# class instances can be annotated as follows
mc : MyClass = MyClass()
mc: MyClass = MyClass()

# tuple packing can be done as follows
tu: Tuple[str, ...] = ('a', 'b', 'c')
Expand Down
17 changes: 10 additions & 7 deletions docs/source/class_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ initialized within the class. Mypy infers the types of attributes:

a = A(1)
a.x = 2 # OK
a.y = 3 # Error: A has no attribute y
a.y = 3 # error: "A" has no attribute "y"

This is a bit like each class having an implicitly defined
``__slots__`` attribute. This is only enforced during type
checking and not when your program is running.

You can declare types of variables in the class body explicitly using
a type comment:
You can declare types of variables in the class body explicitly in the same way
as variables outside of a class body by using a type comment:

.. code-block:: python

class A:
x = None # type: List[int] # Declare attribute x of type List[int]
x = None # type: List[int]

a = A()
a.x = [1] # OK
Expand Down Expand Up @@ -58,7 +58,7 @@ to it explicitly using ``self``:
def __init__(self) -> None:
self.y = 1 # Define y
a = self
a.x = 1 # Error: x not defined
a.x = 1 # error: "A" has no attribute "x"

Overriding statically typed methods
***********************************
Expand All @@ -68,16 +68,19 @@ override has a compatible signature:

.. code-block:: python




class A:
def f(self, x: int) -> None:
...

class B(A):
def f(self, x: str) -> None: # Error: type of x incompatible
def f(self, x: str) -> None: # error: Argument 1 of "f" incompatible with supertype "A"
...

class C(A):
def f(self, x: int, y: int) -> None: # Error: too many arguments
def f(self, x: int, y: int) -> None: # error: Signature of "f" incompatible with supertype "A"
...

class D(A):
Expand Down
54 changes: 31 additions & 23 deletions docs/source/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ flagged as an error.
.. code-block:: python

def foo(a: str) -> str:
return '(' + a.split() + ')'
# error: Unsupported operand types for + ("str" and List[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:

Expand Down Expand Up @@ -98,7 +98,7 @@ module:

.. code-block:: python

import frobnicate # Error: No module "frobnicate"
import frobnicate # error: No module "frobnicate"
frobnicate.start()

You can add a ``# type: ignore`` comment to tell mypy to ignore this
Expand All @@ -123,14 +123,17 @@ Types of empty collections
--------------------------

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

.. code-block:: python

a = [] # type: List[int]
a = [] # error: Need type annotation for variable

Without the annotation mypy can't always figure out the
precise type of ``a``.
Without an annotation mypy can't always figure out the precise type of ``a``.

.. code-block:: python

a = [] # type: List[int]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make more sense to flip the two examples.

Something like the following (I'm omitting the exact words there)


1
--
You often need to specify the type...:

a = []  # type: List[int]

Without the annotation...

a = []  # error: Need type annotation for variable


You can use a simple empty list literal in a dynamically typed function (as the
type of ``a`` would be implicitly ``Any`` and need not be inferred), if type
Expand Down Expand Up @@ -161,7 +164,8 @@ with the ``Any`` type.
def f() -> None:
n = 1
...
n = 'x' # Type error: n has type int
n = 'x' # error: Incompatible types in assignment (expression
# has type "str", variable has type "int")

.. note::

Expand Down Expand Up @@ -197,15 +201,16 @@ unexpected errors when combined with type inference. For example:

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
lst = new_lst # error: Incompatible types in assignment (expression
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth mentioning that mypy complains about the assignment because of the fact that List is invariant.

# has type List[B], variable has type List[A])

Possible strategies in such situations are:

* Use an explicit type annotation:

.. code-block:: python

new_lst: List[A] = [B(), B()]
new_lst = [B(), B()] # type: List[A]
lst = new_lst # OK

* Make a copy of the right hand side:
Expand All @@ -220,7 +225,8 @@ Possible strategies in such situations are:

def f_bad(x: List[A]) -> A:
return x[0]
f_bad(new_lst) # Fails
f_bad(new_lst) # error: Argument 1 to "f_bad" has incompatible
# type List[B]; expected List[A]

def f_good(x: Sequence[A]) -> A:
return x[0]
Expand All @@ -238,17 +244,18 @@ of a name (assume here that ``Shape`` is the base class of both

shape = Circle() # Infer shape to be Circle
...
shape = Triangle() # Type error: Triangle is not a Circle
shape = 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 the
above example:
You can give an explicit type for the variable in cases such the above example:

.. code-block:: python

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

Complex type tests
------------------
Expand All @@ -262,7 +269,8 @@ explicit type cast:
def f(o: object) -> None:
if type(o) is int:
o = cast(int, o)
g(o + 1) # This would be an error without the cast
g(o + 1) # This would be an error without the cast:
# error: Unsupported operand types for + ("object" and "int")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's confusing to write out the error since it doesn't actually happen. I would just leave this comment as is.

Alternatively, you can have the following:

g(o + 1)  # error: Unsupported operand types for + ("object" and "int")
o = cast(int, o)
g(o + 1)  # This would be an error without the cast

...
else:
...
Expand All @@ -285,11 +293,11 @@ style anyway). We can write the above code without a cast by using

def f(o: object) -> None:
if isinstance(o, int): # Mypy understands isinstance checks
g(o + 1) # Okay; type of o is inferred as int here
g(o + 1) # Okay; type of o is inferred as int here
...

Type inference in mypy is designed to work well in common cases, to be
predictable and to let the type checker give useful error
predictable, and to let the type checker give useful error
messages. More powerful type inference strategies often have complex
and difficult-to-predict failure modes and could result in very
confusing error messages. The tradeoff is that you as a programmer
Expand Down Expand Up @@ -368,9 +376,9 @@ understand how mypy handles a particular piece of code. Example:
.. note::

``reveal_type`` is only understood by mypy and doesn't exist
in Python, if you try to run your program. You'll have to remove
any ``reveal_type`` calls before you can run your code.
``reveal_type`` is always available and you don't need to import it.
in Python. You'll have to remove any ``reveal_type`` calls before you run
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove all reveal_type calls

your code. ``reveal_type`` is always available and you don't need to import
it.

.. _import-cycles:

Expand Down
15 changes: 9 additions & 6 deletions docs/source/dynamic_typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ dynamically typed by defining it explicitly with the type ``Any``:

s = 1 # Statically typed (type int)
d = 1 # type: Any # Dynamically typed (type Any)
s = 'x' # Type check error
s = 'x' # error: Incompatible types in assignment
# (expression has type "str", variable has type "int")
d = 'x' # OK

Operations on Any values
Expand Down Expand Up @@ -74,11 +75,13 @@ operations:
.. code-block:: python

def f(o: object) -> None:
o.foo() # Error!
o + 2 # Error!
open(o) # Error!
n = 1 # type: int
n = o # Error!
o.foo() # error: "object" has no attribute "foo"
o + 2 # error: Unsupported operand types for + ("object" and "int")
open(o) # error: Argument 1 to "open" has incompatible type "object";
# expected "Union[str, bytes, int, _PathLike[Any]]"
n = 1 # type: int
n = o # error: Incompatible types in assignment
# (expression has type "object", variable has type "int")

You can use ``cast()`` (see chapter :ref:`casts`) or ``isinstance`` to
go from a general type such as ``object`` to a more specific
Expand Down
Loading