Skip to content
Closed
Changes from 3 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
70 changes: 49 additions & 21 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: List[int] = []

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,7 +201,8 @@ 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:

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: Shape = Circle() # The variable s can be any Shape,
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I agree with changing this -- it uses syntax that only works in Python 3.6.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was per @ilevkivskyi 's suggestion, which is why I added the new section at the end about falling back to the pre 3.6 syntax. Perhaps I can demonstrate both in the code example?

Copy link
Member

Choose a reason for hiding this comment

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

I prefer to use the same stance as the PY3 cheatsheet: use # type: comments in most places, call out the new PEP 526 syntax in one place.

Backwards compatibility is still important to most people!

Copy link
Member

Choose a reason for hiding this comment

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

I agree we don't need to change existing docs with respect to PEP 526 syntax.
(Although, I think it is OK to use it from time to time in new docs.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good! Sorry if I misunderstood. I'll take out the section I added, use # type everywhere, and then mention PEP 526 once in the initial introduction of the variable typing.

# 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 @@ -289,7 +297,7 @@ style anyway). We can write the above code without a cast by using
...

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 Expand Up @@ -430,3 +438,23 @@ Here's the above example modified to use ``MYPY``:

def listify(arg: 'bar.BarClass') -> 'List[bar.BarClass]':
return [arg]

.. _variable-types:

Variable definition type syntax
-------------------------------

If running a Python older than 3.6, the following type declaration will fail:

.. code-block:: python

x: List[str] = [] # SyntaxError: invalid syntax


You can instead use this format in older Pythons (including 2):

.. code-block:: python

x = [] # type: List[str]