Error missing return statement mypy

I am getting a MyPy error "Missing return statement", even when I check for all possible cases inside a function. For example, in the following code, MyPy is still giving me an error "9: error: Mi...

I am getting a MyPy error «Missing return statement», even when I check for all possible cases inside a function.

For example, in the following code, MyPy is still giving me an error "9: error: Missing return statement", even though color can only be Color.RED, Color.GREEN, or Color.BLUE, and I test all those cases!

class Color(enum.IntEnum):
    RED: int = 1
    GREEN: int = 2
    BLUE: int = 3


def test_enum(color: Color) -> str:
    if color == Color.RED:
        return "red"
    elif color == Color.GREEN:
        return "green"
    elif color == Color.BLUE:
        return "blue"

asked Nov 7, 2017 at 2:42

ostrokach's user avatar

6

There really is no question in this question — mypy indeed behaves this way at the moment. The enum support is baked in, and is preliminary and somewhat ad-hoc. The kind of checking you are looking for might be implemented in the future.

However, this code is fragile; if Color will change, it will silently break.
Remember that Python is not a compiled language — the typechecker pass is optional, and someone else might not use it.

The right way IMO is to add assert False at the end. This will also silence mypy.

answered Nov 8, 2017 at 17:19

Elazar's user avatar

ElazarElazar

19.7k4 gold badges44 silver badges67 bronze badges

Содержание

  1. Name already in use
  2. mypy / docs / source / error_code_list.rst
  3. Error codes enabled by default#
  4. Check that attribute exists [attr-defined]#
  5. Check that attribute exists in each union item [union-attr]#
  6. Check that name is defined [name-defined]#
  7. Check arguments in calls [call-arg]#
  8. Check argument types [arg-type]#
  9. Check calls to overloaded functions [call-overload]#
  10. Check validity of types [valid-type]#
  11. Require annotation if variable type is unclear [var-annotated]#
  12. Check validity of overrides [override]#
  13. Check that function returns a value [return]#
  14. Check that return value is compatible [return-value]#
  15. Check types in assignment statement [assignment]#
  16. Check type variable values [type-var]#
  17. Check uses of various operators [operator]#
  18. Check indexing operations [index]#
  19. Check list items [list-item]#
  20. Check dict items [dict-item]#
  21. Check TypedDict items [typeddict-item]#
  22. Check that type of target is known [has-type]#
  23. Check that import target can be found [import]#
  24. Check that each name is defined once [no-redef]#
  25. Check that called function returns a value [func-returns-value]#
  26. Check instantiation of abstract classes [abstract]#
  27. Safe handling of abstract type object types [type-abstract]#
  28. Check that call to an abstract method via super is valid [safe-super]#
  29. Check the target of NewType [valid-newtype]#
  30. Check the return type of __exit__ [exit-return]#
  31. Check that naming is consistent [name-match]#
  32. Check that overloaded functions have an implementation [no-overload-impl]#
  33. Check that coroutine return value is used [unused-coroutine]#
  34. Check types in assert_type [assert-type]#
  35. Report syntax errors [syntax]#
  36. Miscellaneous checks [misc]#

Name already in use

mypy / docs / source / error_code_list.rst

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink

Copy raw contents

Copy raw contents

Error codes enabled by default

This section documents various errors codes that mypy can generate with default options. See :ref:`error-codes` for general documentation about error codes. :ref:`error-codes-optional` documents additional error codes that you can enable.

Check that attribute exists [attr-defined]

Mypy checks that an attribute is defined in the target class or module when using the dot operator. This applies to both getting and setting an attribute. New attributes are defined by assignments in the class body, or assignments to self.x in methods. These assignments don’t generate attr-defined errors.

This error code is also generated if an imported name is not defined in the module in a from . import statement (as long as the target module can be found):

A reference to a missing attribute is given the Any type. In the above example, the type of non_existent will be Any , which can be important if you silence the error.

Check that attribute exists in each union item [union-attr]

If you access the attribute of a value with a union type, mypy checks that the attribute is defined for every type in that union. Otherwise the operation can fail at runtime. This also applies to optional types.

You can often work around these errors by using assert isinstance(obj, ClassName) or assert obj is not None to tell mypy that you know that the type is more specific than what mypy thinks.

Check that name is defined [name-defined]

Mypy expects that all references to names have a corresponding definition in an active scope, such as an assignment, function definition or an import. This can catch missing definitions, missing imports, and typos.

This example accidentally calls sort() instead of :py:func:`sorted` :

Check arguments in calls [call-arg]

Mypy expects that the number and names of arguments match the called function. Note that argument type checks have a separate error code arg-type .

Check argument types [arg-type]

Mypy checks that argument types in a call match the declared argument types in the signature of the called function (if one exists).

Check calls to overloaded functions [call-overload]

When you call an overloaded function, mypy checks that at least one of the signatures of the overload items match the argument types in the call.

Check validity of types [valid-type]

Mypy checks that each type annotation and any expression that represents a type is a valid type. Examples of valid types include classes, union types, callable types, type aliases, and literal types. Examples of invalid types include bare integer literals, functions, variables, and modules.

This example incorrectly uses the function log as a type:

typing.Callable` as the type for callable objects:

Require annotation if variable type is unclear [var-annotated]

In some cases mypy can’t infer the type of a variable without an explicit annotation. Mypy treats this as an error. This typically happens when you initialize a variable with an empty collection or None . If mypy can’t infer the collection item type, mypy replaces any parts of the type it couldn’t infer with Any and generates an error.

Example with an error:

To address this, we add an explicit annotation:

Check validity of overrides [override]

Mypy checks that an overridden method or attribute is compatible with the base class. A method in a subclass must accept all arguments that the base class method accepts, and the return type must conform to the return type in the base class (Liskov substitution principle).

Argument types can be more general is a subclass (i.e., they can vary contravariantly). The return type can be narrowed in a subclass (i.e., it can vary covariantly). It’s okay to define additional arguments in a subclass method, as long all extra arguments have default values or can be left out ( *args , for example).

Check that function returns a value [return]

If a function has a non- None return type, mypy expects that the function always explicitly returns a value (or raises an exception). The function should not fall off the end of the function, since this is often a bug.

Check that return value is compatible [return-value]

Mypy checks that the returned value is compatible with the type signature of the function.

Check types in assignment statement [assignment]

Mypy checks that the assigned expression is compatible with the assignment target (or targets).

Check type variable values [type-var]

Mypy checks that value of a type variable is compatible with a value restriction or the upper bound type.

Check uses of various operators [operator]

Mypy checks that operands support a binary or unary operation, such as + or

. Indexing operations are so common that they have their own error code index (see below).

Check indexing operations [index]

Mypy checks that the indexed value in indexing operation such as x[y] supports indexing, and that the index expression has a valid type.

Check list items [list-item]

When constructing a list using [item, . ] , mypy checks that each item is compatible with the list type that is inferred from the surrounding context.

Check dict items [dict-item]

When constructing a dictionary using or dict(key=value, . ) , mypy checks that each key and value is compatible with the dictionary type that is inferred from the surrounding context.

Check TypedDict items [typeddict-item]

When constructing a TypedDict object, mypy checks that each key and value is compatible with the TypedDict type that is inferred from the surrounding context.

When getting a TypedDict item, mypy checks that the key exists. When assigning to a TypedDict , mypy checks that both the key and the value are valid.

Check that type of target is known [has-type]

Mypy sometimes generates an error when it hasn’t inferred any type for a variable being referenced. This can happen for references to variables that are initialized later in the source file, and for references across modules that form an import cycle. When this happens, the reference gets an implicit Any type.

In this example the definitions of x and y are circular:

To work around this error, you can add an explicit type annotation to the target variable or attribute. Sometimes you can also reorganize the code so that the definition of the variable is placed earlier than the reference to the variable in a source file. Untangling cyclic imports may also help.

We add an explicit annotation to the y attribute to work around the issue:

Check that import target can be found [import]

Mypy generates an error if it can’t find the source code or a stub file for an imported module.

See :ref:`ignore-missing-imports` for how to work around these errors.

Check that each name is defined once [no-redef]

Mypy may generate an error if you have multiple definitions for a name in the same namespace. The reason is that this is often an error, as the second definition may overwrite the first one. Also, mypy often can’t be able to determine whether references point to the first or the second definition, which would compromise type checking.

If you silence this error, all references to the defined name refer to the first definition.

Check that called function returns a value [func-returns-value]

Mypy reports an error if you call a function with a None return type and don’t ignore the return value, as this is usually (but not always) a programming error.

In this example, the if f() check is always false since f returns None :

Check instantiation of abstract classes [abstract]

Mypy generates an error if you try to instantiate an abstract base class (ABC). An abstract base class is a class with at least one abstract method or attribute. (See also :py:mod:`abc` module documentation)

Sometimes a class is made accidentally abstract, often due to an unimplemented abstract method. In a case like this you need to provide an implementation for the method to make the class concrete (non-abstract).

Safe handling of abstract type object types [type-abstract]

Mypy always allows instantiating (calling) type objects typed as Type[t] , even if it is not known that t is non-abstract, since it is a common pattern to create functions that act as object factories (custom constructors). Therefore, to prevent issues described in the above section, when an abstract type object is passed where Type[t] is expected, mypy will give an error. Example:

Check that call to an abstract method via super is valid [safe-super]

Abstract methods often don’t have any default implementation, i.e. their bodies are just empty. Calling such methods in subclasses via super() will cause runtime errors, so mypy prevents you from doing so:

Mypy considers the following as trivial bodies: a pass statement, a literal ellipsis . , a docstring, and a raise NotImplementedError statement.

Check the target of NewType [valid-newtype]

The target of a :py:func:`NewType ` definition must be a class type. It can’t be a union type, Any , or various other special types.

You can also get this error if the target has been imported from a module whose source mypy cannot find, since any such definitions are treated by mypy as values with Any types. Example:

To work around the issue, you can either give mypy access to the sources for acme or create a stub file for the module. See :ref:`ignore-missing-imports` for more information.

Check the return type of __exit__ [exit-return]

If mypy can determine that :py:meth:`__exit__ ` always returns False , mypy checks that the return type is not bool . The boolean value of the return type affects which lines mypy thinks are reachable after a with statement, since any :py:meth:`__exit__ ` method that can return True may swallow exceptions. An imprecise return type can result in mysterious errors reported near with statements.

To fix this, use either typing_extensions.Literal[False] or None as the return type. Returning None is equivalent to returning False in this context, since both are treated as false values.

This produces the following output from mypy:

You can use Literal[False] to fix the error:

You can also use None :

Check that naming is consistent [name-match]

The definition of a named tuple or a TypedDict must be named consistently when using the call-based syntax. Example:

Check that overloaded functions have an implementation [no-overload-impl]

Overloaded functions outside of stub files must be followed by a non overloaded implementation.

Check that coroutine return value is used [unused-coroutine]

Mypy ensures that return values of async def functions are not ignored, as this is usually a programming error, as the coroutine won’t be executed at the call site.

You can work around this error by assigning the result to a temporary, otherwise unused variable:

Check types in assert_type [assert-type]

The inferred type for an expression passed to assert_type must match the provided type.

Check that function isn’t used in boolean context [truthy-function]

Functions will always evaluate to true in boolean contexts.

Report syntax errors [syntax]

If the code being checked is not syntactically valid, mypy issues a syntax error. Most, but not all, syntax errors are blocking errors: they can’t be ignored with a # type: ignore comment.

Miscellaneous checks [misc]

Mypy performs numerous other, less commonly failing checks that don’t have specific error codes. These use the misc error code. Other than being used for multiple unrelated errors, the misc error code is not special. For example, you can ignore all errors in this category by using # type: ignore[misc] comment. Since these errors are not expected to be common, it’s unlikely that you’ll see two different errors with the misc code on a single line — though this can certainly happen once in a while.

Future mypy versions will likely add new error codes for some errors that currently use the misc error code.

Источник

Error codes enabled by default#

This section documents various errors codes that mypy can generate with default options. See Error codes for general documentation about error codes. Error codes for optional checks documents additional error codes that you can enable.

Check that attribute exists [attr-defined]#

Mypy checks that an attribute is defined in the target class or module when using the dot operator. This applies to both getting and setting an attribute. New attributes are defined by assignments in the class body, or assignments to self.x in methods. These assignments don’t generate attr-defined errors.

This error code is also generated if an imported name is not defined in the module in a from . import statement (as long as the target module can be found):

A reference to a missing attribute is given the Any type. In the above example, the type of non_existent will be Any , which can be important if you silence the error.

Check that attribute exists in each union item [union-attr]#

If you access the attribute of a value with a union type, mypy checks that the attribute is defined for every type in that union. Otherwise the operation can fail at runtime. This also applies to optional types.

You can often work around these errors by using assert isinstance(obj, ClassName) or assert obj is not None to tell mypy that you know that the type is more specific than what mypy thinks.

Check that name is defined [name-defined]#

Mypy expects that all references to names have a corresponding definition in an active scope, such as an assignment, function definition or an import. This can catch missing definitions, missing imports, and typos.

This example accidentally calls sort() instead of sorted() :

Check arguments in calls [call-arg]#

Mypy expects that the number and names of arguments match the called function. Note that argument type checks have a separate error code arg-type .

Check argument types [arg-type]#

Mypy checks that argument types in a call match the declared argument types in the signature of the called function (if one exists).

Check calls to overloaded functions [call-overload]#

When you call an overloaded function, mypy checks that at least one of the signatures of the overload items match the argument types in the call.

Check validity of types [valid-type]#

Mypy checks that each type annotation and any expression that represents a type is a valid type. Examples of valid types include classes, union types, callable types, type aliases, and literal types. Examples of invalid types include bare integer literals, functions, variables, and modules.

This example incorrectly uses the function log as a type:

You can use Callable as the type for callable objects:

Require annotation if variable type is unclear [var-annotated]#

In some cases mypy can’t infer the type of a variable without an explicit annotation. Mypy treats this as an error. This typically happens when you initialize a variable with an empty collection or None . If mypy can’t infer the collection item type, mypy replaces any parts of the type it couldn’t infer with Any and generates an error.

Example with an error:

To address this, we add an explicit annotation:

Check validity of overrides [override]#

Mypy checks that an overridden method or attribute is compatible with the base class. A method in a subclass must accept all arguments that the base class method accepts, and the return type must conform to the return type in the base class (Liskov substitution principle).

Argument types can be more general is a subclass (i.e., they can vary contravariantly). The return type can be narrowed in a subclass (i.e., it can vary covariantly). It’s okay to define additional arguments in a subclass method, as long all extra arguments have default values or can be left out ( *args , for example).

Check that function returns a value [return]#

If a function has a non- None return type, mypy expects that the function always explicitly returns a value (or raises an exception). The function should not fall off the end of the function, since this is often a bug.

Check that return value is compatible [return-value]#

Mypy checks that the returned value is compatible with the type signature of the function.

Check types in assignment statement [assignment]#

Mypy checks that the assigned expression is compatible with the assignment target (or targets).

Check type variable values [type-var]#

Mypy checks that value of a type variable is compatible with a value restriction or the upper bound type.

Check uses of various operators [operator]#

Mypy checks that operands support a binary or unary operation, such as + or

. Indexing operations are so common that they have their own error code index (see below).

Check indexing operations [index]#

Mypy checks that the indexed value in indexing operation such as x[y] supports indexing, and that the index expression has a valid type.

Check list items [list-item]#

When constructing a list using [item, . ] , mypy checks that each item is compatible with the list type that is inferred from the surrounding context.

Check dict items [dict-item]#

When constructing a dictionary using or dict(key=value, . ) , mypy checks that each key and value is compatible with the dictionary type that is inferred from the surrounding context.

Check TypedDict items [typeddict-item]#

When constructing a TypedDict object, mypy checks that each key and value is compatible with the TypedDict type that is inferred from the surrounding context.

When getting a TypedDict item, mypy checks that the key exists. When assigning to a TypedDict , mypy checks that both the key and the value are valid.

Check that type of target is known [has-type]#

Mypy sometimes generates an error when it hasn’t inferred any type for a variable being referenced. This can happen for references to variables that are initialized later in the source file, and for references across modules that form an import cycle. When this happens, the reference gets an implicit Any type.

In this example the definitions of x and y are circular:

To work around this error, you can add an explicit type annotation to the target variable or attribute. Sometimes you can also reorganize the code so that the definition of the variable is placed earlier than the reference to the variable in a source file. Untangling cyclic imports may also help.

We add an explicit annotation to the y attribute to work around the issue:

Check that import target can be found [import]#

Mypy generates an error if it can’t find the source code or a stub file for an imported module.

See Missing imports for how to work around these errors.

Check that each name is defined once [no-redef]#

Mypy may generate an error if you have multiple definitions for a name in the same namespace. The reason is that this is often an error, as the second definition may overwrite the first one. Also, mypy often can’t be able to determine whether references point to the first or the second definition, which would compromise type checking.

If you silence this error, all references to the defined name refer to the first definition.

Check that called function returns a value [func-returns-value]#

Mypy reports an error if you call a function with a None return type and don’t ignore the return value, as this is usually (but not always) a programming error.

In this example, the if f() check is always false since f returns None :

Check instantiation of abstract classes [abstract]#

Mypy generates an error if you try to instantiate an abstract base class (ABC). An abstract base class is a class with at least one abstract method or attribute. (See also abc module documentation)

Sometimes a class is made accidentally abstract, often due to an unimplemented abstract method. In a case like this you need to provide an implementation for the method to make the class concrete (non-abstract).

Safe handling of abstract type object types [type-abstract]#

Mypy always allows instantiating (calling) type objects typed as Type[t] , even if it is not known that t is non-abstract, since it is a common pattern to create functions that act as object factories (custom constructors). Therefore, to prevent issues described in the above section, when an abstract type object is passed where Type[t] is expected, mypy will give an error. Example:

Check that call to an abstract method via super is valid [safe-super]#

Abstract methods often don’t have any default implementation, i.e. their bodies are just empty. Calling such methods in subclasses via super() will cause runtime errors, so mypy prevents you from doing so:

Mypy considers the following as trivial bodies: a pass statement, a literal ellipsis . , a docstring, and a raise NotImplementedError statement.

Check the target of NewType [valid-newtype]#

The target of a NewType definition must be a class type. It can’t be a union type, Any , or various other special types.

You can also get this error if the target has been imported from a module whose source mypy cannot find, since any such definitions are treated by mypy as values with Any types. Example:

To work around the issue, you can either give mypy access to the sources for acme or create a stub file for the module. See Missing imports for more information.

Check the return type of __exit__ [exit-return]#

If mypy can determine that __exit__ always returns False , mypy checks that the return type is not bool . The boolean value of the return type affects which lines mypy thinks are reachable after a with statement, since any __exit__ method that can return True may swallow exceptions. An imprecise return type can result in mysterious errors reported near with statements.

To fix this, use either typing_extensions.Literal[False] or None as the return type. Returning None is equivalent to returning False in this context, since both are treated as false values.

This produces the following output from mypy:

You can use Literal[False] to fix the error:

You can also use None :

Check that naming is consistent [name-match]#

The definition of a named tuple or a TypedDict must be named consistently when using the call-based syntax. Example:

Check that overloaded functions have an implementation [no-overload-impl]#

Overloaded functions outside of stub files must be followed by a non overloaded implementation.

Check that coroutine return value is used [unused-coroutine]#

Mypy ensures that return values of async def functions are not ignored, as this is usually a programming error, as the coroutine won’t be executed at the call site.

You can work around this error by assigning the result to a temporary, otherwise unused variable:

Check types in assert_type [assert-type]#

The inferred type for an expression passed to assert_type must match the provided type.

Report syntax errors [syntax]#

If the code being checked is not syntactically valid, mypy issues a syntax error. Most, but not all, syntax errors are blocking errors: they can’t be ignored with a # type: ignore comment.

Miscellaneous checks [misc]#

Mypy performs numerous other, less commonly failing checks that don’t have specific error codes. These use the misc error code. Other than being used for multiple unrelated errors, the misc error code is not special. For example, you can ignore all errors in this category by using # type: ignore[misc] comment. Since these errors are not expected to be common, it’s unlikely that you’ll see two different errors with the misc code on a single line – though this can certainly happen once in a while.

Future mypy versions will likely add new error codes for some errors that currently use the misc error code.

Источник

Welcome to day 28 of the 30 Days of Python series! Today we’re going to be learning about type hinting in Python using type annotations.

Type annotations allow us to tell Python what we expect to be assigned to names at given points in our application. We can then use these annotations to check the program is in fact doing what we intend.

The benefits of type hinting

Type hinting can be very helpful for a number of reasons.

  1. It makes it easier to understand our code, because our helpful variable names are now accompanied by a description of the sort of data we expect to be assigned to them.
  2. Most modern editors are able to make good use of our type annotations to provide more meaningful hints when we do things like calling functions.
  3. We can use tools like mypy to check that our type annotations are being honoured, helping us catch bugs caused by passing around incorrect types.

One thing type annotations cannot do is actually prevent us from breaking the rules we outlined in our annotations. They’re a development tool only, and don’t have any effect on our code when we run the application.

Now that we have an idea about what type annotations can do for us, let’s learn how to actually use them.

Installing mypy

If you’re using an editor like PyCharm, type hinting is built in and ready to go. PyCharm will give you live hints while writing your code, so you don’t need to do anything extra.

If your editor doesn’t support this kind of live type hinting, or you want to be able to run a tool to give you comprehensive information about a given file, we need to install a tool like mypy.

We can install mypy by running the following command:

python -m pip install mypy

If you’re not sure how to use this, there’s a tutorial in the Python documentation that talks about installing packages using the command line.

Once mypy has been installed, we can use it by writing mypy followed by the name of the file we want to check. For example, if we wanted to check app.py, we’d write the following in the console:

This will inform us of any problems that mypy was able to detect while evaluating this file.

Basic type hinting

Let’s start by using type hinting to annotate some variables that we expect to take basic types.

name: str = "Phil"
age: int = 29
height_metres: float = 1.87
loves_python: bool = True

As you can see from the examples above, we can annotate a variable by adding a colon after the variable name, after which we specify the type.

Here I’ve indicated that name is a string, age is an integer, and height_metres should be a float. In this case, all of our type annotations align with the values that have been assigned, so we don’t have any issues.

We can confirm this by running mypy.

> mypy app.py
Success: no issues found in 1 source file

Now let’s look at what happens when things are not what we intended.

name: str = 29
age: int = 1.87
height_metres: float = "Phil"

All of our values have gotten jumbled up. Maybe we assigned these values from an iterable that contained the values in the wrong order.

Now if we run mypy, we get some errors.

> mypy app.py
app.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
app.py:2: error: Incompatible types in assignment (expression has type "float", variable has type "int")
app.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "float")
Found 3 errors in 1 file (checked 1 source file)

The errors are actually very helpful. They explain exactly what went wrong, and at the start of each line we get a reference to the file where the error happened, along with a line number.

Using this information, we can easily track down the source of this issue.

Adding some flexibility

Let’s say we want to be a little more flexible in how we handle height_metres. I only really care that it’s a real number, so instead of accepting just floats, I want to also accept integers.

The way we accomplish this is by using a tool called Union which we have to import from the typing module.

from typing import Union

name: str = "Phil"
age: int = 29
height_metres: Union[int, float] = 1.87

Here I’ve added Union[int, float] as a type annotation for height_metres, which means we can accept either integers or floats. We can add as many types as we like to this Union by adding more comma separated values between the square brackets.

We can also get super flexible and use another tool called Any, which matched any type. You should be careful about using Any, because it largely removed the benefits of type hinting. It can be useful for indicating to readers that something is entirely generic though.

We can use Any like any of the other types:

from typing import Any

name: str = "Phil"
age: int = 29
height_metres: Any = 1.87

Annotating collections

Now that we’ve looked at annotating basic types, let’s talk about how we might annotate that something should be a list, or maybe a tuple containing values of a specific type.

In order to annotate collections, we have to import special types from the typing module. For lists we need to import List and for tuples we need to import Tuple.

As you can see, the names make a lot of sense, but if you’re ever in doubt about what you need to import, you can find the documentation for the typing module here.

Here is an example of a variable using a List annotation:

from typing import List

names: List =  ["Rick", "Morty", "Summer", "Beth", "Jerry"]

If we wanted to specify which types should in the list, we can add a set of square brackets, much like we did with Union.

from typing import List

names: List[str] =  ["Rick", "Morty", "Summer", "Beth", "Jerry"]

Leaving the brackets off is really the same thing as writing List[Any].

If we want, we can allow a variety of types in a list by combining List and Union like this:

from typing import List, Union

random_values: List[Union[str, int]] =  ["x", 13, "camel", 0]

When working with tuples, we can specify a type for each item in sequence, since tuples are of fixed length and are immutable.

For example, we can do something like this:

from typing import Tuple

movie: Tuple[str, str, int] = ("Toy Story 3", "Lee Unkrich", 2010)

Note

In Python 3.9 we won’t have to import things like Tuple, List, and Dict from the typing module. Instead, we’ll be able to use the standard tuple, list, and dict types for annotation.

You can find out more here.

Creating type aliases

Let’s consider a case where we want to store lots of movies like the one above in a list. We ran into this case several times in the early stages of the course. How would we annotate something like that?

Maybe something like this:

from typing import List, Tuple

movies: List[Tuple[str, str, int]] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

This does work, but that type annotation is getting very hard to read. That’s a problem, because one of the benefits of using type annotations is to help with readability.

In cases like this where we have complex type annotations, it’s often better to define new aliases for certain type combinations. For example, I think it makes a lot of sense to call each of these tuples a Movie.

We can do this like so:

from typing import List, Tuple

Movie = Tuple[str, str, int]

movies: List[Movie] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

Now tools like mypy are going to consider the term Movie as meaning Tuple[str, str, int]. If we try checking our code with mypy, we can see everything works just fine.

> mypy app.py
Success: no issues found in 1 source file

Now let’s make a small change, just so we can assure ourselves that we’re not getting false positives. To check, I’m going to change the date of Finding Nemo to the string, "2005".

When we run mypy, we now can an error, just as we expected.

> mypy app.py
app.py:11: error: List item 0 has incompatible type "Tuple[str, str, str]"; expected "Tuple[str, str, int]"
Found 1 error in 1 file (checked 1 source file)

Annotating functions

Now let’s get into annotating functions, which is where this kind of tool is most useful. Let’s stick with our list of movie tuples for now, and let’s add a function to print each movie in a given format.

from typing import List, Tuple

Movie = Tuple[str, str, int]

def show_movies(movies):
    for title, director, year in movies:
        print(f"{title} ({year}), by {director}")

movies: List[Movie] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

show_movies(movies)

So, how do we annotate this function?

Annotating parameters is just like annotating any other variable. In this case we’re passing in a list of movie tuples, and we already have a Movie annotation ready to go, so we can just write this:

from typing import List, Tuple

Movie = Tuple[str, str, int]

def show_movies(movies: List[Movie]):
    for title, director, year in movies:
        print(f"{title} ({year}), by {director}")

movies: List[Movie] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

show_movies(movies)

Annotating return values

Let’s add another couple of functions to our little application. I want to add a search function to determine if a given movie exists in the movie library, and I also want to add a function to handle the printing of a single movie.

My implementation is going to look something like this:

from typing import List, Tuple

Movie = Tuple[str, str, int]

def find_movie(search_term, movies):
    for title, director, year in movies:
        if title == search_term:
            return (title, director, year)

def show_movies(movies: List[Movie]):
    for movie in movies:
        print_movie(movie)

def print_movie(movie):
    title, director, year = movie
    print(f"{title} ({year}), by {director}")

movies: List[Movie] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

show_movies(movies)

search_result = find_movie("Finding Nemo", movies)

if search_result:
    print_movie(search_result)
else:
    print("Couldn't find movie.")

The find_movie function is relatively crude. It returns only perfect matches, and it can find only a single result, but it will do for his example.

The print_movie function is now dealing with a small part of what the original show_movies function was doing. Defining this second function allows us to print individual movies in other parts of our application, like when we get a movie back from search_result.

Let’s start by annotating all the things we already know how to do.

from typing import List, Tuple, Union

Movie = Tuple[str, str, int]

def find_movie(search_term: str, movies: List[Movie]):
    for title, director, year in movies:
        if title == search_term:
            return (title, director, year)

def show_movies(movies: List[Movie]):
    for movie in movies:
        print_movie(movie)

def print_movie(movie: Movie):
    title, director, year = movie
    print(f"{title} ({year}), by {director}")

movies: List[Movie] = [
    ("Finding Nemo", "Andrew Stanton", 2005),
    ("Inside Out", "Pete Docter", 2015),
    ("Toy Story 3", "Lee Unkrich", 2010)
]

show_movies(movies)

search_result: Union[Movie, None] = find_movie("Finding Nemo", movies)

if search_result:
    print_movie(search_result)
else:
    print("Couldn't find movie.")

Run this through mypy a few times with different values and make sure everything still works.

Now that we have the majority of the app type annotated, there’s one thing we’re missing. We’re not currently telling Python what our functions should return.

We can do this by using -> after the parentheses when defining our functions.

Most of our functions don’t need a return annotation, because they implicitly return. However, our find_movie function does, and it can return two different values: a movie tuple, or None. It returns None in the case where no matching movie was found.

We can therefore annotate it like this:

def find_movie(search_term: str, movies: List[Movie]) -> Union[Movie, None]:
    for title, director, year in movies:
        if title == search_term:
            return (title, director, year)

We have a problem though. If we run mypy, it complains that we’re missing a return statement.

> mypy app.py
app.py:5: error: Missing return statement
Found 1 error in 1 file (checked 1 source file)

This is mypy warning us that we’ve done something not in keeping with the Python style guide (PEP8). To quote the guide,

Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. If any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None, and an explicit return statement should be present at the end of the function (if reachable)

This means we should be writing our function like this:

def find_movie(search_term: str, movies: List[Movie]) -> Union[Movie, None]:
    for title, director, year in movies:
        if title == search_term:
            return (title, director, year)

    return None

Now everything passes without issue:

> mypy app.py
Success: no issues found in 1 source file

Using Optional

In cases where have something like Union[Movie, None], where one of the types in a Union is None, we have another tool we can use called Optional. If we write something like Optional[Movie], this is the same thing as writing Union[Movie, None].

def find_movie(search_term: str, movies: List[Movie]) -> Optional[Movie]:
    for title, director, year in movies:
        if title == search_term:
            return (title, director, year)

    return None

Exercises

There’s only one exercise today, because it’s a big one. Take your final solution to the day 14 project (easy or hard version) and implement type hinting for your code.

If you’re unsure of how to do something, remember that you can always look at the typing module documentation.

You can find our solution here.

Project

Today is another project day, so once you’re done with today’s exercise, make sure to check out today’s project!

Today we’re going to be writing a program to automatically gather information from pages on the Internet.

Я пытаюсь реализовать функцию повтора в http_requests, но у меня возникают проблемы с «необходимым» оператором возврата, хотя я не могу понять, где это должно быть.

def _http_request(method: _HttpMethod, url: str, **kwargs) -> Optional[requests.models.Response]:
    _body: Any = None
    _retries: Any = 3
    if "json" in kwargs:
        _body = kwargs.get("json")
    elif "data" in kwargs:
        _body = kwargs.get("data")
    elif "retries" in kwargs:
        _retries = kwargs.get("retries")
    elif "timeout" in kwargs:
        _timeout = kwargs.get("timeout")

    for _retry in range(1, _retries):
        try:
            logging.debug(f"{method.value} {url} body: {_body}")
            _response = requests.request(method.value, url, **kwargs)
            logging.debug(f"{_response.status_code} body: {_response.text}")
            return _response
        except NewConnectionError:
            logging.error("Failed to establish a new connection: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time.")
            return None
        except requests.exceptions.ConnectionError:
            logging.error("Failed to establish a new connection: [Errno 113] No route to host.")
            return None
        except requests.exceptions.Timeout:
            if _retry <= 3:
                logging.warning(f"Connection to Jira timed out after {_timeout}, trying to connect again({_retry} of {_retries} retries)")
            else:
                logging.error(f"Failed to connect to jira server efter {_retries}")
                return None

Каков полный текст сообщения об ошибке.


— Andy G

14.02.2019 10:34

Возможно, связано с stackoverflow.com/questions/47149154/…


— match

14.02.2019 10:35

Энди Г. — Сообщение об ошибке: Missing return statement _mypy(error)_ отображается на уровне определения метода без указания того, где потребуется оператор возврата.


— Xoferif

14.02.2019 10:44

Скраппинг поиска Apple App Store с помощью Python

Редкие достижения на Github ✨

Мутабельность и переработка объектов в Python

Другой маршрут в Flask Python

Другой маршрут в Flask Python

Flask — это фреймворк, который поддерживает веб-приложения. В этой статье я покажу, как мы можем использовать @app .route в flask, чтобы иметь другую…

14 Задание: Типы данных и структуры данных Python для DevOps

Python PyPDF2 - запись метаданных PDF


Ответы
1

Ответ принят как подходящий

Операторы return находятся внутри цикла for, но не после него, что создает несоответствие.

Добавьте return None вне (после) цикла for.

Блок if _retry <= 3: также несогласован в том, что он не имеет оператора возврата, но return None после цикла может разрешить предупреждение.

Хотя я не могу понять, почему MyPy не может видеть операторы возврата в цикле, похоже, это решило мою проблему, хотя мне не нравится, как это выглядит, но это работает. (:


— Xoferif

14.02.2019 10:56

Если бы цикл никогда не был введен, то метод не встречал бы оператор возврата. Мы видим, что вход в цикл будет всегда, потому что _retries присвоено значение 3, но анализатор не может (или не будет) это определить.


— Andy G

14.02.2019 10:58

Другие вопросы по теме

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Error missing resource file
  • Error missing required parameters sitekey
  • Error missing required parameter q
  • Error missing required argument search query
  • Error missing radix parameter radix

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии