From f96446ce2f99a86210b21d39c7aec4b13a8bfc66 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 22 Dec 2021 10:39:36 +0000 Subject: [PATCH] Various small documentation updates (#11817) Improve consistency, grammar, formatting and clarity in the documentation. Also some minor content fixes. --- docs/source/class_basics.rst | 22 +- docs/source/command_line.rst | 4 +- docs/source/common_issues.rst | 204 +++++++++--------- docs/source/generics.rst | 48 +++-- docs/source/installed_packages.rst | 7 +- docs/source/kinds_of_types.rst | 42 ++-- docs/source/literal_types.rst | 42 ++-- docs/source/more_types.rst | 6 +- docs/source/stubtest.rst | 20 +- .../source/type_inference_and_annotations.rst | 16 +- 10 files changed, 227 insertions(+), 184 deletions(-) diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 83177e26aa67..3c12b4b06d9b 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -320,8 +320,8 @@ Slots ***** When a class has explicitly defined -`__slots__ `_ -mypy will check that all attributes assigned to are members of `__slots__`. +`__slots__ `_, +mypy will check that all attributes assigned to are members of ``__slots__``: .. code-block:: python @@ -331,13 +331,19 @@ mypy will check that all attributes assigned to are members of `__slots__`. def __init__(self, name: str, year: int) -> None: self.name = name self.year = year - self.released = True # E: Trying to assign name "released" that is not in "__slots__" of type "Album" + # Error: Trying to assign name "released" that is not in "__slots__" of type "Album" + self.released = True my_album = Album('Songs about Python', 2021) -Mypy will only check attribute assignments against `__slots__` when the following conditions hold: +Mypy will only check attribute assignments against ``__slots__`` when +the following conditions hold: -1. All base classes (except builtin ones) must have explicit ``__slots__`` defined (mirrors CPython's behaviour) -2. ``__slots__`` does not include ``__dict__``, since if ``__slots__`` includes ``__dict__`` - it allows setting any attribute, similar to when ``__slots__`` is not defined (mirrors CPython's behaviour) -3. All values in ``__slots__`` must be statically known. For example, no variables: only string literals. +1. All base classes (except builtin ones) must have explicit + ``__slots__`` defined (this mirrors Python semantics). + +2. ``__slots__`` does not include ``__dict__``. If ``__slots__`` + includes ``__dict__``, arbitrary attributes can be set, similar to + when ``__slots__`` is not defined (this mirrors Python semantics). + +3. All values in ``__slots__`` must be string literals. diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 7405ab563e0d..a729ac2baca0 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -312,8 +312,8 @@ The following options are available: .. option:: --disallow-any-generics This flag disallows usage of generic types that do not specify explicit - type parameters. For example you can't use a bare ``x: list``, you must say - ``x: list[int]``. + type parameters. For example, you can't use a bare ``x: list``. Instead, you + must always write something like ``x: list[int]``. .. option:: --disallow-subclassing-any diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 9d6423137c41..c4adc9b563bd 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -26,102 +26,102 @@ No errors reported for obviously wrong code There are several common reasons why obviously wrong code is not flagged as an error. -- **The function containing the error is not annotated.** Functions that - do not have any annotations (neither for any argument nor for the - return type) are not type-checked, and even the most blatant type - errors (e.g. ``2 + 'a'``) pass silently. The solution is to add - annotations. Where that isn't possible, functions without annotations - can be checked using :option:`--check-untyped-defs `. +**The function containing the error is not annotated.** Functions that +do not have any annotations (neither for any argument nor for the +return type) are not type-checked, and even the most blatant type +errors (e.g. ``2 + 'a'``) pass silently. The solution is to add +annotations. Where that isn't possible, functions without annotations +can be checked using :option:`--check-untyped-defs `. - Example: +Example: - .. code-block:: python +.. code-block:: python - def foo(a): - return '(' + a.split() + ')' # No error! + 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 reported - once you add annotations: +This gives no error even though ``a.split()`` is "obviously" a list +(the author probably meant ``a.strip()``). The error is reported +once you add annotations: - .. code-block:: python +.. code-block:: python - def foo(a: str) -> str: - return '(' + a.split() + ')' - # error: Unsupported operand types for + ("str" and List[str]) + 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: +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 above - example, if we were to leave out the annotation for ``a``, we'd get - no error: +**One of the values involved has type 'Any'.** Extending the above +example, if we were to leave out the annotation for ``a``, we'd get +no error: - .. code-block:: python +.. code-block:: python - def foo(a) -> str: - return '(' + a.split() + ')' # No error! + def foo(a) -> str: + return '(' + a.split() + ')' # No error! - The reason is that if the type of ``a`` is unknown, the type of - ``a.split()`` is also unknown, so it is inferred as having type - ``Any``, and it is no error to add a string to an ``Any``. +The reason is that if the type of ``a`` is unknown, the type of +``a.split()`` is also unknown, so it is inferred as having type +``Any``, and it is no error to add a string to an ``Any``. - If you're having trouble debugging such situations, - :ref:`reveal_type() ` might come in handy. +If you're having trouble debugging such situations, +:ref:`reveal_type() ` might come in handy. - Note that sometimes library stubs have imprecise type information, - e.g. the :py:func:`pow` builtin returns ``Any`` (see `typeshed issue 285 - `_ for the reason). +Note that sometimes library stubs have imprecise type information, +e.g. the :py:func:`pow` builtin returns ``Any`` (see `typeshed issue 285 +`_ for the reason). -- :py:meth:`__init__ ` **method has no annotated - arguments or return type annotation.** :py:meth:`__init__ ` - is considered fully-annotated **if at least one argument is annotated**, - while mypy will infer the return type as ``None``. - The implication is that, for a :py:meth:`__init__ ` method - that has no argument, you'll have to explicitly annotate the return type - as ``None`` to type-check this :py:meth:`__init__ ` method: +:py:meth:`__init__ ` **method has no annotated +arguments or return type annotation.** :py:meth:`__init__ ` +is considered fully-annotated **if at least one argument is annotated**, +while mypy will infer the return type as ``None``. +The implication is that, for a :py:meth:`__init__ ` method +that has no argument, you'll have to explicitly annotate the return type +as ``None`` to type-check this :py:meth:`__init__ ` method: - .. code-block:: python +.. code-block:: python - 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 of - unexpected ``Any`` values are the :option:`--ignore-missing-imports - ` and :option:`--follow-imports=skip - ` flags. When you use :option:`--ignore-missing-imports `, - any imported module that cannot be found is silently replaced with - ``Any``. When using :option:`--follow-imports=skip ` the same is true for - modules for which a ``.py`` file is found but that are not specified - on the command line. (If a ``.pyi`` stub is found it is always - processed normally, regardless of the value of - :option:`--follow-imports `.) To help debug the former situation (no - module found at all) leave out :option:`--ignore-missing-imports `; to get - clarity about the latter use :option:`--follow-imports=error `. You can - read up about these and other useful flags in :ref:`command-line`. - -- **A function annotated as returning a non-optional type returns 'None' - and mypy doesn't complain**. + 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 of +unexpected ``Any`` values are the :option:`--ignore-missing-imports +` and :option:`--follow-imports=skip +` flags. When you use :option:`--ignore-missing-imports `, +any imported module that cannot be found is silently replaced with +``Any``. When using :option:`--follow-imports=skip ` the same is true for +modules for which a ``.py`` file is found but that are not specified +on the command line. (If a ``.pyi`` stub is found it is always +processed normally, regardless of the value of +:option:`--follow-imports `.) To help debug the former situation (no +module found at all) leave out :option:`--ignore-missing-imports `; to get +clarity about the latter use :option:`--follow-imports=error `. You can +read up about these and other useful flags in :ref:`command-line`. + +**A function annotated as returning a non-optional type returns 'None' +and mypy doesn't complain**. - .. code-block:: python +.. code-block:: python - def foo() -> str: - return None # No error! + def foo() -> str: + return None # No error! - You may have disabled strict optional checking (see - :ref:`no_strict_optional` for more). +You may have disabled strict optional checking (see +:ref:`no_strict_optional` for more). .. _silencing_checker: @@ -383,10 +383,10 @@ explicit type cast: if index < 0: raise ValueError('No str found') - found = a[index] # Has `object` type, despite the fact that we know it is `str` - return cast(str, found) # So, we need an explicit cast to make mypy happy + found = a[index] # Has type "object", despite the fact that we know it is "str" + return cast(str, found) # We need an explicit cast to make mypy happy -Alternatively, you can use ``assert`` statement together with some +Alternatively, you can use an ``assert`` statement together with some of the supported type inference techniques: .. code-block:: python @@ -396,9 +396,9 @@ of the supported type inference techniques: if index < 0: raise ValueError('No str found') - found = a[index] # Has `object` type, despite the fact that we know it is `str` - assert isinstance(found, str) # Now, `found` will be narrowed to `str` subtype - return found # No need for the explicit `cast()` anymore + found = a[index] # Has type "object", despite the fact that we know it is "str" + assert isinstance(found, str) # Now, "found" will be narrowed to "str" + return found # No need for the explicit "cast()" anymore .. note:: @@ -411,7 +411,7 @@ of the supported type inference techniques: .. note:: - You can read more about type narrowing techniques here. + You can read more about type narrowing techniques :ref:`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 @@ -634,32 +634,41 @@ You can install the latest development version of mypy from source. Clone the Variables vs type aliases ------------------------- -Mypy has both type aliases and variables with types like ``Type[...]`` and it is important to know their difference. +Mypy has both *type aliases* and variables with types like ``Type[...]``. These are +subtly different, and it's important to understand how they differ to avoid pitfalls. -1. Variables with type ``Type[...]`` should be created by assignments with an explicit type annotations: +1. A variable with type ``Type[...]`` is defined using an assignment with an + explicit type annotation: .. code-block:: python class A: ... tp: Type[A] = A -2. Aliases are created by assignments without an explicit type. +2. You can define a type alias using an assignment without an explicit type annotation + at the top level of a module: .. code-block:: python class A: ... Alias = A - Or you can also use :pep:`613` and explicit type aliases: + You can also use ``TypeAlias`` (:pep:`613`) to define an *explicit type alias*: .. code-block:: python - - from typing import TypeAlias # or `from typing_extensions` before `python3.10` - + + from typing import TypeAlias # "from typing_extensions" in Python 3.9 and earlier + class A: ... Alias: TypeAlias = A -3. The difference is that aliases are completely known statically and can be used in type context (annotations): + You should always use ``TypeAlias`` to define a type alias in a class body or + inside a function. + +The main difference is that the target of an alias is precisely known statically, and this +means that they can be used in type annotations and other *type contexts*. Type aliases +can't be defined conditionally (unless using +:ref:`supported Python version and platform checks `): .. code-block:: python @@ -669,17 +678,18 @@ Mypy has both type aliases and variables with types like ``Type[...]`` and it is if random() > 0.5: Alias = A else: - Alias = B # error: Cannot assign multiple types to name "Alias" without an explicit "Type[...]" annotation \ - # error: Incompatible types in assignment (expression has type "Type[B]", variable has type "Type[A]") + # error: Cannot assign multiple types to name "Alias" without an + # explicit "Type[...]" annotation + Alias = B - tp: Type[object] # tp is a type variable + tp: Type[object] # "tp" is a variable with a type object value if random() > 0.5: tp = A else: tp = B # This is OK - def fun1(x: Alias) -> None: ... # This is OK - def fun2(x: tp) -> None: ... # error: Variable "__main__.tp" is not valid as a type + def fun1(x: Alias) -> None: ... # OK + def fun2(x: tp) -> None: ... # Error: "tp" is not valid as a type Incompatible overrides ---------------------- diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 84582be13f88..7e64aa181403 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -77,8 +77,8 @@ Generic class internals *********************** You may wonder what happens at runtime when you index -``Stack``. Actually, indexing ``Stack`` returns essentially a copy -of ``Stack`` that returns instances of the original class on +``Stack``. Indexing ``Stack`` returns a *generic alias* +to ``Stack`` that returns instances of the original class on instantiation: .. code-block:: python @@ -90,11 +90,20 @@ instantiation: >>> print(Stack[int]().__class__) __main__.Stack -For Python 3.8 and lower, note that built-in types :py:class:`list`, -:py:class:`dict` and so on do not support indexing in Python. -This is why we have the aliases :py:class:`~typing.List`, :py:class:`~typing.Dict` -and so on in the :py:mod:`typing` module. Indexing these aliases gives -you a class that directly inherits from the target class in Python: +Generic aliases can be instantiated or subclassed, similar to real +classes, but the above examples illustrate that type variables are +erased at runtime. Generic ``Stack`` instances are just ordinary +Python objects, and they have no extra runtime overhead or magic due +to being generic, other than a metaclass that overloads the indexing +operator. + +Note that in Python 3.8 and lower, the built-in types +:py:class:`list`, :py:class:`dict` and others do not support indexing. +This is why we have the aliases :py:class:`~typing.List`, +:py:class:`~typing.Dict` and so on in the :py:mod:`typing` +module. Indexing these aliases gives you a generic alias that +resembles generic aliases constructed by directly indexing the target +class in more recent versions of Python: .. code-block:: python @@ -103,15 +112,24 @@ you a class that directly inherits from the target class in Python: >>> from typing import List >>> List[int] typing.List[int] - >>> List[int].__bases__ - (, typing.MutableSequence) -Generic types could be instantiated or subclassed as usual classes, -but the above examples illustrate that type variables are erased at -runtime. Generic ``Stack`` instances are just ordinary -Python objects, and they have no extra runtime overhead or magic due -to being generic, other than a metaclass that overloads the indexing -operator. +Note that the generic aliases in ``typing`` don't support constructing +instances: + +.. code-block:: python + + >>> from typing import List + >>> List[int]() + Traceback (most recent call last): + ... + TypeError: Type List cannot be instantiated; use list() instead + +.. note:: + + In Python 3.6 indexing generic types or type aliases results in actual + type objects. This means that generic types in type annotations can + have a significant runtime cost. This was changed in Python 3.7, and + indexing generic types became a cheap operation. .. _generic-subclasses: diff --git a/docs/source/installed_packages.rst b/docs/source/installed_packages.rst index 036185c818e5..8db113e4ba9e 100644 --- a/docs/source/installed_packages.rst +++ b/docs/source/installed_packages.rst @@ -178,9 +178,10 @@ the Python 2 stubs in a directory with the suffix ``-python2-stubs``. We recommend that Python 2 and Python 3 stubs are bundled together for simplicity, instead of distributing them separately. -The instructions are enough to ensure that built wheels contains the appropriate -files. However, to ensure inclusion inside the ``sdist`` (``.tar.gz`` archive), -you may also need to modify the inclusion rules in your ``MANIFEST.in``: +The instructions above are enough to ensure that the built wheels +contain the appropriate files. However, to ensure inclusion inside the +``sdist`` (``.tar.gz`` archive), you may also need to modify the +inclusion rules in your ``MANIFEST.in``: .. code-block:: text diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index 73a6acac1db8..866535949d74 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -521,21 +521,25 @@ assigning the type to a variable: another type -- it's equivalent to the target type except for :ref:`generic aliases `. -Since Mypy 0.930 you can also use explicit type aliases which are defined by :pep:`613`. +Since Mypy 0.930 you can also use *explicit type aliases*, which were +introduced in :pep:`613`. -Implicit type alias declaration rules create confusion when type aliases involve forward references, -invalid types, or violate other restrictions enforced on type alias declaration. -Because the distinction between an unannotated value and a type alias is implicit, -ambiguous or incorrect type alias declarations implicitly default to a valid value assignment. +There can be confusion about exactly when an assignment defines an implicit type alias -- +for example, when the alias contains forward references, invalid types, or violates some other +restrictions on type alias declarations. Because the +distinction between an unannotated variable and a type alias is implicit, +ambiguous or incorrect type alias declarations default to defining +a normal variable instead of a type alias. + +Explicit type aliases are unambiguous and can also improve readability by +making the intent clear: .. code-block:: python - from typing import TypeAlias # or `from typing_extensions` before `python3.10` + from typing import TypeAlias # "from typing_extensions" in Python 3.9 and earlier AliasType: TypeAlias = Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]] -Explicit type aliases are unambiguous and improve readability. - .. _named-tuples: Named tuples @@ -578,8 +582,8 @@ Python 3.6 introduced an alternative, class-based syntax for named tuples with t .. note:: - You can use raw ``NamedTuple`` pseudo-class to annotate type - where any ``NamedTuple`` is expected. + You can use the raw ``NamedTuple`` "pseudo-class" in type annotations + if any ``NamedTuple`` object is valid. For example, it can be useful for deserialization: @@ -594,10 +598,12 @@ Python 3.6 introduced an alternative, class-based syntax for named tuples with t deserialize_named_tuple(Point(x=1, y=2)) # ok deserialize_named_tuple(Person(name='Nikita', age=18)) # ok - deserialize_named_tuple((1, 2)) # Argument 1 to "deserialize_named_tuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple" + # Error: Argument 1 to "deserialize_named_tuple" has incompatible type + # "Tuple[int, int]"; expected "NamedTuple" + deserialize_named_tuple((1, 2)) - Note, that behavior is highly experimental, non-standard, - and can be not supported by other type checkers. + Note that this behavior is highly experimental, non-standard, + and may not be supported by other type checkers and IDEs. .. _type-of-class: @@ -743,15 +749,15 @@ type of either :py:class:`Iterator[YieldType] ` or :py:class:`I def squares(n: int) -> Iterator[int]: for i in range(n): yield i * i - + A good rule of thumb is to annotate functions with the most specific return type possible. However, you should also take care to avoid leaking implementation -details into a function's public API. In keeping with these two principles, prefer +details into a function's public API. In keeping with these two principles, prefer :py:class:`Iterator[YieldType] ` over -:py:class:`Iterable[YieldType] ` as the return-type annotation for a -generator function, as it lets mypy know that users are able to call :py:func:`next` on +:py:class:`Iterable[YieldType] ` as the return-type annotation for a +generator function, as it lets mypy know that users are able to call :py:func:`next` on the object returned by the function. Nonetheless, bear in mind that ``Iterable`` may -sometimes be the better option, if you consider it an implementation detail that +sometimes be the better option, if you consider it an implementation detail that ``next()`` can be called on the object returned by your function. If you want your generator to accept values via the :py:meth:`~generator.send` method or return diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index 94eec06236ca..b1669d01062a 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -142,7 +142,7 @@ as adding an explicit ``Literal[...]`` annotation, it often leads to the same ef in practice. The main cases where the behavior of context-sensitive vs true literal types differ are -when you try using those types in places that are not explicitly expecting a ``Literal[...]``. +when you try using those types in places that are not explicitly expecting a ``Literal[...]``. For example, compare and contrast what happens when you try appending these types to a list: .. code-block:: python @@ -208,7 +208,7 @@ corresponding to some particular index, we can use Literal types like so: # You can also index using unions of literals id_key: Literal["main_id", "backup_id"] - reveal_type(d[id_key]) # Revealed type is "int" + reveal_type(d[id_key]) # Revealed type is "int" .. _tagged_unions: @@ -248,7 +248,7 @@ type. Then, you can discriminate between each kind of TypedDict by checking the # Literal["new-job", "cancel-job"], but the check below will narrow # the type to either Literal["new-job"] or Literal["cancel-job"]. # - # This in turns narrows the type of 'event' to either NewJobEvent + # This in turns narrows the type of 'event' to either NewJobEvent # or CancelJobEvent. if event["tag"] == "new-job": print(event["job_name"]) @@ -289,11 +289,11 @@ using ``isinstance()``: This feature is sometimes called "sum types" or "discriminated union types" in other programming languages. -Exhaustive checks -***************** +Exhaustiveness checks +********************* -One may want to check that some code covers all possible ``Literal`` or ``Enum`` cases, -example: +You may want to check that some code covers all possible +``Literal`` or ``Enum`` cases. Example: .. code-block:: python @@ -306,21 +306,22 @@ example: return True elif x == 'two': return False - raise ValueError('Wrong values passed: {0}'.format(x)) + raise ValueError(f'Invalid value: {x}') assert validate('one') is True assert validate('two') is False -In the code above it is really easy to make a mistake in the future: -by adding a new literal value to ``PossibleValues``, -but not adding its handler to ``validate`` function: +In the code above, it's easy to make a mistake. You can +add a new literal value to ``PossibleValues`` but forget +to handle it in the ``validate`` function: .. code-block:: python PossibleValues = Literal['one', 'two', 'three'] -Mypy won't catch that ``'three'`` is not covered. -However, if you want to have exhaustive check, you need to guard it properly: +Mypy won't catch that ``'three'`` is not covered. If you want mypy to +perform an exhaustiveness check, you need to update your code to use an +``assert_never()`` check: .. code-block:: python @@ -329,8 +330,8 @@ However, if you want to have exhaustive check, you need to guard it properly: PossibleValues = Literal['one', 'two'] def assert_never(value: NoReturn) -> NoReturn: - # This also works in runtime as well: - assert False, 'This code should never be reached, got: {0}'.format(value) + # This also works at runtime as well + assert False, f'This code should never be reached, got: {value}' def validate(x: PossibleValues) -> bool: if x == 'one': @@ -339,22 +340,21 @@ However, if you want to have exhaustive check, you need to guard it properly: return False assert_never(x) -In this case, when adding new values to ``PossibleValues``: +Now if you add a new value to ``PossibleValues`` but don't update ``validate``, +mypy will spot the error: .. code-block:: python PossibleValues = Literal['one', 'two', 'three'] -Mypy will cover you: - -.. code-block:: python - def validate(x: PossibleValues) -> bool: if x == 'one': return True elif x == 'two': return False - assert_never(x) # E: Argument 1 to "assert_never" has incompatible type "Literal['three']"; expected "NoReturn" + # Error: Argument 1 to "assert_never" has incompatible type "Literal['three']"; + # expected "NoReturn" + assert_never(x) Limitations *********** diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 11dbef32cbfa..82a6568afcb2 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -294,10 +294,10 @@ return type by using overloads like so: subtypes, you can use a :ref:`value restriction `. -The default values of a function's arguments don't affect its signature, only +The default values of a function's arguments don't affect its signature -- only the absence or presence of a default value does. So in order to reduce -redundancy it's possible to replace default values in overload definitions with -`...` as a placeholder. +redundancy, it's possible to replace default values in overload definitions with +``...`` as a placeholder: .. code-block:: python diff --git a/docs/source/stubtest.rst b/docs/source/stubtest.rst index 5903de2cf7ca..828931fbdf2b 100644 --- a/docs/source/stubtest.rst +++ b/docs/source/stubtest.rst @@ -17,24 +17,24 @@ implementation at runtime. What stubtest does and does not do ********************************** -stubtest will import your code and introspect your code objects at runtime, for -example, by using the capabilities of the :py:mod:`inspect` module. stubtest +Stubtest will import your code and introspect your code objects at runtime, for +example, by using the capabilities of the :py:mod:`inspect` module. Stubtest will then analyse the stub files, and compare the two, pointing out things that differ between stubs and the implementation at runtime. -It's important to be aware of the limitations of this comparison. stubtest will +It's important to be aware of the limitations of this comparison. Stubtest will not make any attempt to statically analyse your actual code and relies only on dynamic runtime introspection (in particular, this approach means stubtest works well with extension modules). However, this means that stubtest has limited visibility; for instance, it cannot tell if a return type of a function is accurately typed in the stubs. -For clarity, here are some more things stubtest does not do: +For clarity, here are some additional things stubtest can't do: -* Type check your code, use ``mypy`` -* Generate stubs, use ``stubgen`` or ``pyright --createstub`` -* Generate stubs based on running your application or test suite, use ``monkeytype`` -* Apply stubs to code to produce inline types, use ``retype`` or ``libcst`` +* Type check your code -- use ``mypy`` instead +* Generate stubs -- use ``stubgen`` or ``pyright --createstub`` instead +* Generate stubs based on running your application or test suite -- use ``monkeytype`` instead +* Apply stubs to code to produce inline types -- use ``retype`` or ``libcst`` instead In summary, stubtest works very well for ensuring basic consistency between stubs and implementation or to check for stub completeness. It's used to @@ -81,11 +81,11 @@ Usage Running stubtest can be as simple as ``stubtest module_to_check``. Run :option:`stubtest --help` for a quick summary of options. -stubtest must be able to import the code to be checked, so make sure that mypy +Subtest must be able to import the code to be checked, so make sure that mypy is installed in the same environment as the library to be tested. In some cases, setting ``PYTHONPATH`` can help stubtest find the code to import. -Similarly, stubtest must be able to find the stubs to be checked. stubtest +Similarly, stubtest must be able to find the stubs to be checked. Stubtest respects the ``MYPYPATH`` environment variable. If you wish to ignore some of stubtest's complaints, stubtest supports a diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst index 80b9356cd681..8150f88e579e 100644 --- a/docs/source/type_inference_and_annotations.rst +++ b/docs/source/type_inference_and_annotations.rst @@ -95,10 +95,11 @@ Similarly, you can also give an explicit type when creating an empty set: .. note:: - Using type annotations (e.g. `list[int]`) on builtin collections like + Using type arguments (e.g. ``list[int]``) on builtin collections like :py:class:`list`, :py:class:`dict`, :py:class:`tuple`, and :py:class:`set` only works in Python 3.9 and later. For Python 3.8 and earlier, you must use - :py:class:`~typing.List`, :py:class:`~typing.Dict`, etc. + :py:class:`~typing.List` (e.g. ``List[int]``), :py:class:`~typing.Dict`, and + so on. Compatibility of container types @@ -267,19 +268,20 @@ short explanation of the bug. To do that, use this format: app.run(8000) # type: ignore # `run()` now accepts an `int`, as a port -If your error displays an error code, like so: +Mypy displays an error code for each error if you use +:option:`--show-error-codes `: .. code-block:: text error: "str" has no attribute "trim" [attr-defined] -It is possible to add a specific error-code in your ignore comment, like -``# type: ignore[attr-defined]``, to clarify what's being silenced. You can -find more information about error codes here: :ref:`silence-error-codes`. +It is possible to add a specific error-code in your ignore comment (e.g. +``# type: ignore[attr-defined]``) to clarify what's being silenced. You can +find more information about error codes :ref:`here `. Similarly, you can also ignore all mypy checks in a file, by adding a -``# type: ignore`` on the top of the file: +``# type: ignore`` at the top of the file: .. code-block:: python