diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 035109df7e37..6e6e442e4ad5 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -17,6 +17,7 @@ The simplest way to narrow a type is to use one of the supported expressions: - :py:func:`isinstance` like in ``isinstance(obj, float)`` will narrow ``obj`` to have ``float`` type - :py:func:`issubclass` like in ``issubclass(cls, MyClass)`` will narrow ``cls`` to be ``Type[MyClass]`` - :py:func:`type` like in ``type(obj) is int`` will narrow ``obj`` to have ``int`` type +- :py:func:`callable` like in ``callable(obj)`` will narrow object to callable type Type narrowing is contextual. For example, based on the condition, mypy will narrow an expression only within an ``if`` branch: @@ -57,6 +58,34 @@ We can also use ``assert`` to narrow types in the same context: assert isinstance(arg, int) reveal_type(arg) # Revealed type: "builtins.int" +.. note:: + + With :option:`--warn-unreachable ` + narrowing types to some impossible state will be treated as an error. + + .. code-block:: python + + def function(arg: int): + # error: Subclass of "int" and "str" cannot exist: + # would have incompatible method signatures + assert isinstance(arg, str) + + # error: Statement is unreachable + print("so mypy concludes the assert will always trigger") + + Without ``--warn-unreachable`` mypy will simply not check code it deems to be + unreachable. See :ref:`unreachable` for more information. + + .. code-block:: python + + x: int = 1 + assert isinstance(x, str) + reveal_type(x) # Revealed type is "builtins.int" + print(x + '!') # Typechecks with `mypy`, but fails in runtime. + +issubclass +~~~~~~~~~~ + Mypy can also use :py:func:`issubclass` for better type inference when working with types and metaclasses: @@ -75,31 +104,36 @@ for better type inference when working with types and metaclasses: reveal_type(t) # Revealed type is "Type[MyCalcMeta]" t.calc() # Okay -.. note:: +callable +~~~~~~~~ - With :option:`--warn-unreachable ` - narrowing types to some impossible state will be treated as an error. +Mypy knows what types are callable and which ones are not during type checking. +So, we know what ``callable()`` will return. For example: - .. code-block:: python +.. code-block:: python - def function(arg: int): - # error: Subclass of "int" and "str" cannot exist: - # would have incompatible method signatures - assert isinstance(arg, str) + from typing import Callable - # error: Statement is unreachable - print("so mypy concludes the assert will always trigger") + x: Callable[[], int] - Without ``--warn-unreachable`` mypy will simply not check code it deems to be - unreachable. See :ref:`unreachable` for more information. + if callable(x): + reveal_type(x) # N: Revealed type is "def () -> builtins.int" + else: + ... # Will never be executed and will raise error with `--warn-unreachable` - .. code-block:: python +``callable`` function can even split ``Union`` type +for callable and non-callable parts: - x: int = 1 - assert isinstance(x, str) - reveal_type(x) # Revealed type is "builtins.int" - print(x + '!') # Typechecks with `mypy`, but fails in runtime. +.. code-block:: python + + from typing import Callable, Union + + x: Union[int, Callable[[], int]] + if callable(x): + reveal_type(x) # N: Revealed type is "def () -> builtins.int" + else: + reveal_type(x) # N: Revealed type is "builtins.int" .. _casts: