Skip to content

Commit

Permalink
Various documentation and error message tweaks (#14574)
Browse files Browse the repository at this point in the history
I looked at `git diff v0.991 master -- docs` and did some editing. There
no major changes to content. Also updated one error message.
  • Loading branch information
JukkaL authored and hauntsaninja committed Feb 3, 2023
1 parent 9aa1776 commit 8ef98cc
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 41 deletions.
42 changes: 27 additions & 15 deletions docs/source/error_code_list2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ Example:
Check that methods do not have redundant Self annotations [redundant-self]
--------------------------------------------------------------------------

Such annotations are allowed by :pep:`673` but are redundant, so if you want
warnings about them, enable this error code.
If a method uses the ``Self`` type in the return type or the type of a
non-self argument, there is no need to annotate the ``self`` argument
explicitly. Such annotations are allowed by :pep:`673` but are
redundant. If you enable this error code, mypy will generate an error if
there is a redundant ``Self`` type.

Example:

Expand All @@ -97,7 +100,7 @@ Example:
from typing import Self
class C:
# Error: Redundant Self annotation on method first argument
# Error: Redundant "Self" annotation for the first method argument
def copy(self: Self) -> Self:
return type(self)()
Expand Down Expand Up @@ -236,29 +239,34 @@ mypy generates an error if it thinks that an expression is redundant.
Check that expression is not implicitly true in boolean context [truthy-bool]
-----------------------------------------------------------------------------

Warn when an expression whose type does not implement ``__bool__`` or ``__len__`` is used in boolean context,
since unless implemented by a sub-type, the expression will always evaluate to true.
Warn when the type of an expression in a boolean context does not
implement ``__bool__`` or ``__len__``. Unless one of these is
implemented by a subtype, the expression will always be considered
true, and there may be a bug in the condition.

As an exception, the ``object`` type is allowed in a boolean context.
Using an iterable value in a boolean context has a separate error code
(see below).

.. code-block:: python
# Use "mypy --enable-error-code truthy-bool ..."
class Foo:
pass
pass
foo = Foo()
# Error: "foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context
if foo:
...
The check is similar in concept to ensuring that an expression's type implements an expected interface (e.g. ``Sized``),
except that attempting to invoke an undefined method (e.g. ``__len__``) results in an error,
while attempting to evaluate an object in boolean context without a concrete implementation results in a truthy value.
...
Check that iterable is not implicitly true in boolean context [truthy-iterable]
-------------------------------------------------------------------------------

``Iterable`` does not implement ``__len__`` and so this code will be flagged:
Generate an error if a value of type ``Iterable`` is used as a boolean
condition, since ``Iterable`` does not implement ``__len__`` or ``__bool__``.

Example:

.. code-block:: python
Expand All @@ -270,9 +278,13 @@ Check that iterable is not implicitly true in boolean context [truthy-iterable]
return [42]
return [x + 1 for x in items]
If called with a ``Generator`` like ``int(x) for x in []``, this function would not return ``[42]`` unlike
what the author might have intended. Of course it's possible that ``transform`` is only passed ``list`` objects,
and so there is no error in practice. In such case, it is recommended to annotate ``items: Collection[int]``.
If ``transform`` is called with a ``Generator`` argument, such as
``int(x) for x in []``, this function would not return ``[42]`` unlike
what might be intended. Of course, it's possible that ``transform`` is
only called with ``list`` or other container objects, and the ``if not
items`` check is actually valid. If that is the case, it is
recommended to annotate ``items`` as ``Collection[int]`` instead of
``Iterable[int]``.


.. _ignore-without-code:
Expand Down
51 changes: 29 additions & 22 deletions docs/source/generics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,11 @@ Generic methods and generic self
********************************

You can also define generic methods — just use a type variable in the
method signature that is different from class type variables. In particular,
``self`` may also be generic, allowing a method to return the most precise
type known at the point of access. In this way, for example, you can typecheck
chaining of setter methods:
method signature that is different from class type variables. In
particular, the ``self`` argument may also be generic, allowing a
method to return the most precise type known at the point of access.
In this way, for example, you can type check a chain of setter
methods:

.. code-block:: python
Expand All @@ -222,7 +223,9 @@ chaining of setter methods:
circle: Circle = Circle().set_scale(0.5).set_radius(2.7)
square: Square = Square().set_scale(0.5).set_width(3.2)
Without using generic ``self``, the last two lines could not be type-checked properly.
Without using generic ``self``, the last two lines could not be type
checked properly, since the return type of ``set_scale`` would be
``Shape``, which doesn't define ``set_radius`` or ``set_width``.

Other uses are factory methods, such as copy and deserialization.
For class methods, you can also define generic ``cls``, using :py:class:`Type[T] <typing.Type>`:
Expand Down Expand Up @@ -255,16 +258,18 @@ In the latter case, you must implement this method in all future subclasses.
Note also that mypy cannot always verify that the implementation of a copy
or a deserialization method returns the actual type of self. Therefore
you may need to silence mypy inside these methods (but not at the call site),
possibly by making use of the ``Any`` type.
possibly by making use of the ``Any`` type or a ``# type: ignore`` comment.

Note that this feature may accept some unsafe code for the purpose of
*practicality*. For example:
Note that mypy lets you use generic self types in certain unsafe ways
in order to support common idioms. For example, using a generic
self type in an argument type is accepted even though it's unsafe:

.. code-block:: python
from typing import TypeVar
T = TypeVar("T")
class Base:
def compare(self: T, other: T) -> bool:
return False
Expand All @@ -273,25 +278,27 @@ Note that this feature may accept some unsafe code for the purpose of
def __init__(self, x: int) -> None:
self.x = x
# This is unsafe (see below), but allowed because it is
# a common pattern, and rarely causes issues in practice.
# This is unsafe (see below) but allowed because it's
# a common pattern and rarely causes issues in practice.
def compare(self, other: Sub) -> bool:
return self.x > other.x
b: Base = Sub(42)
b.compare(Base()) # Runtime error here: 'Base' object has no attribute 'x'
For some advanced uses of self-types see :ref:`additional examples <advanced_self>`.
For some advanced uses of self types, see :ref:`additional examples <advanced_self>`.

Automatic self types using typing.Self
**************************************

The patterns described above are quite common, so there is a syntactic sugar
for them introduced in :pep:`673`. Instead of defining a type variable and
using an explicit ``self`` annotation, you can import a magic type ``typing.Self``
that is automatically transformed into a type variable with an upper bound of
current class, and you don't need an annotation for ``self`` (or ``cls`` for
class methods). The above example can thus be rewritten as:
Since the patterns described above are quite common, mypy supports a
simpler syntax, introduced in :pep:`673`, to make them easier to use.
Instead of defining a type variable and using an explicit annotation
for ``self``, you can import the special type ``typing.Self`` that is
automatically transformed into a type variable with the current class
as the upper bound, and you don't need an annotation for ``self`` (or
``cls`` in class methods). The example from the previous section can
be made simpler by using ``Self``:

.. code-block:: python
Expand All @@ -312,13 +319,13 @@ class methods). The above example can thus be rewritten as:
a, b = SuperFriend.make_pair()
This is more compact than using explicit type variables, plus additionally
you can use ``Self`` in attribute annotations, not just in methods.
This is more compact than using explicit type variables. Also, you can
use ``Self`` in attribute annotations in addition to methods.

.. note::

To use this feature on versions of Python before 3.11, you will need to
import ``Self`` from ``typing_extensions`` version 4.0 or newer.
To use this feature on Python versions earlier than 3.11, you will need to
import ``Self`` from ``typing_extensions`` (version 4.0 or newer).

.. _variance-of-generics:

Expand Down Expand Up @@ -911,7 +918,7 @@ defeating the purpose of using aliases. Example:
OIntVec = Optional[Vec[int]]
Using type variable bounds or values in generic aliases, has the same effect
Using type variable bounds or values in generic aliases has the same effect
as in generic classes/functions.


Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ understand, debug, and maintain.

.. note::

Although mypy is production ready, there will be occasional changes
Although mypy is production ready, there may be occasional changes
that break backward compatibility. The mypy development team tries to
minimize the impact of changes to user code. In case of a major breaking
change, mypy's major version will be bumped.
Expand Down
2 changes: 1 addition & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type:
# This error is off by default, since it is explicitly allowed
# by the PEP 673.
self.fail(
"Redundant Self annotation on method first argument",
'Redundant "Self" annotation for the first method argument',
func,
code=codes.REDUNDANT_SELF_TYPE,
)
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-selftype.test
Original file line number Diff line number Diff line change
Expand Up @@ -1627,13 +1627,13 @@ class C:
from typing import Self, Type

class C:
def copy(self: Self) -> Self: # E: Redundant Self annotation on method first argument
def copy(self: Self) -> Self: # E: Redundant "Self" annotation for the first method argument
d: Defer
class Defer: ...
return self

@classmethod
def g(cls: Type[Self]) -> Self: # E: Redundant Self annotation on method first argument
def g(cls: Type[Self]) -> Self: # E: Redundant "Self" annotation for the first method argument
d: DeferAgain
class DeferAgain: ...
return cls()
Expand Down

0 comments on commit 8ef98cc

Please sign in to comment.